http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/ModuleNode.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/ModuleNode.java b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java new file mode 100644 index 0000000..a0f4b52 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/ModuleNode.java @@ -0,0 +1,488 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import groovy.lang.Binding; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstructorCallExpression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.classgen.GeneratorContext; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.runtime.InvokerHelper; +import org.codehaus.groovy.transform.BaseScriptASTTransformation; +import org.objectweb.asm.Opcodes; + +import java.io.File; +import java.net.URI; +import java.net.URISyntaxException; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; + +/** + * Represents a module, which consists typically of a class declaration + * but could include some imports, some statements and multiple classes + * intermixed with statements like scripts in Python or Ruby + * + * @author Jochen Theodorou + * @author Paul King + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class ModuleNode extends ASTNode implements Opcodes { + + private final BlockStatement statementBlock = new BlockStatement(); + List<ClassNode> classes = new LinkedList<ClassNode>(); + private final List<MethodNode> methods = new ArrayList<MethodNode>(); + private final Map<String, ImportNode> imports = new HashMap<String, ImportNode>(); + private final List<ImportNode> starImports = new ArrayList<ImportNode>(); + private final Map<String, ImportNode> staticImports = new LinkedHashMap<String, ImportNode>(); + private final Map<String, ImportNode> staticStarImports = new LinkedHashMap<String, ImportNode>(); + private CompileUnit unit; + private PackageNode packageNode; + private String description; + private boolean createClassForStatements = true; + private transient SourceUnit context; + private boolean importsResolved = false; + private ClassNode scriptDummy; + private String mainClassName = null; + private final Parameter[] SCRIPT_CONTEXT_CTOR = {new Parameter(ClassHelper.BINDING_TYPE, "context")}; + + public ModuleNode (SourceUnit context ) { + this.context = context; + } + + public ModuleNode (CompileUnit unit) { + this.unit = unit; + } + + public BlockStatement getStatementBlock() { + return statementBlock; + } + + public List<MethodNode> getMethods() { + return methods; + } + + public List<ClassNode> getClasses() { + if (createClassForStatements && (!statementBlock.isEmpty() || !methods.isEmpty() || isPackageInfo())) { + ClassNode mainClass = createStatementsClass(); + mainClassName = mainClass.getName(); + createClassForStatements = false; + classes.add(0, mainClass); + mainClass.setModule(this); + addToCompileUnit(mainClass); + } + return classes; + } + + private boolean isPackageInfo() { + return context != null && context.getName() != null && context.getName().endsWith("package-info.groovy"); + } + + public List<ImportNode> getImports() { + return new ArrayList<ImportNode>(imports.values()); + } + + public List<ImportNode> getStarImports() { + return starImports; + } + + /** + * @param alias the name of interest + * @return the class node for the given alias or null if none is available + */ + public ClassNode getImportType(String alias) { + ImportNode importNode = imports.get(alias); + return importNode == null ? null : importNode.getType(); + } + + /** + * @param alias the name of interest + * @return the import node for the given alias or null if none is available + */ + public ImportNode getImport(String alias) { + return imports.get(alias); + } + + public void addImport(String alias, ClassNode type) { + addImport(alias, type, new ArrayList<AnnotationNode>()); + } + + public void addImport(String alias, ClassNode type, List<AnnotationNode> annotations) { + ImportNode importNode = new ImportNode(type, alias); + imports.put(alias, importNode); + importNode.addAnnotations(annotations); + storeLastAddedImportNode(importNode); + } + + public void addStarImport(String packageName) { + addStarImport(packageName, new ArrayList<AnnotationNode>()); + } + + public void addStarImport(String packageName, List<AnnotationNode> annotations) { + ImportNode importNode = new ImportNode(packageName); + importNode.addAnnotations(annotations); + starImports.add(importNode); + storeLastAddedImportNode(importNode); + } + + public void addStatement(Statement node) { + statementBlock.addStatement(node); + } + + public void addClass(ClassNode node) { + if(classes.isEmpty()) mainClassName = node.getName(); + classes.add(node); + node.setModule(this); + addToCompileUnit(node); + } + + private void addToCompileUnit(ClassNode node) { + // register the new class with the compile unit + if (unit != null) { + unit.addClass(node); + } + } + + public void addMethod(MethodNode node) { + methods.add(node); + } + + public void visit(GroovyCodeVisitor visitor) { + } + + public String getPackageName() { + return packageNode == null ? null : packageNode.getName(); + } + + public PackageNode getPackage() { + return packageNode; + } + + // TODO don't allow override? + public void setPackage(PackageNode packageNode) { + this.packageNode = packageNode; + } + + // TODO don't allow override? + public void setPackageName(String packageName) { + this.packageNode = new PackageNode(packageName); + } + + public boolean hasPackageName(){ + return packageNode != null && packageNode.getName() != null; + } + + public boolean hasPackage(){ + return this.packageNode != null; + } + + public SourceUnit getContext() { + return context; + } + + /** + * @return the underlying character stream description + */ + public String getDescription() { + if( context != null ) + { + return context.getName(); + } + else + { + return this.description; + } + } + + public void setDescription(String description) { + this.description = description; + } + + public CompileUnit getUnit() { + return unit; + } + + void setUnit(CompileUnit unit) { + this.unit = unit; + } + + public ClassNode getScriptClassDummy() { + if (scriptDummy!=null) { + setScriptBaseClassFromConfig(scriptDummy); + return scriptDummy; + } + + String name = getPackageName(); + if (name == null) { + name = ""; + } + // now let's use the file name to determine the class name + if (getDescription() == null) { + throw new RuntimeException("Cannot generate main(String[]) class for statements when we have no file description"); + } + name += GeneratorContext.encodeAsValidClassName(extractClassFromFileDescription()); + + ClassNode classNode; + if (isPackageInfo()) { + classNode = new ClassNode(name, ACC_ABSTRACT | ACC_INTERFACE, ClassHelper.OBJECT_TYPE); + } else { + classNode = new ClassNode(name, ACC_PUBLIC, ClassHelper.SCRIPT_TYPE); + setScriptBaseClassFromConfig(classNode); + classNode.setScript(true); + classNode.setScriptBody(true); + } + + scriptDummy = classNode; + return classNode; + } + + private void setScriptBaseClassFromConfig(ClassNode cn) { + String baseClassName = null; + if (unit != null) { + baseClassName = unit.getConfig().getScriptBaseClass(); + } else if (context != null) { + baseClassName = context.getConfiguration().getScriptBaseClass(); + } + if (baseClassName != null) { + if (!cn.getSuperClass().getName().equals(baseClassName)) { + cn.setSuperClass(ClassHelper.make(baseClassName)); + AnnotationNode annotationNode = new AnnotationNode(BaseScriptASTTransformation.MY_TYPE); + cn.addAnnotation(annotationNode); + } + } + } + + protected ClassNode createStatementsClass() { + ClassNode classNode = getScriptClassDummy(); + if (classNode.getName().endsWith("package-info")) { + return classNode; + } + + handleMainMethodIfPresent(methods); + + // return new Foo(new ShellContext(args)).run() + classNode.addMethod( + new MethodNode( + "main", + ACC_PUBLIC | ACC_STATIC, + ClassHelper.VOID_TYPE, + new Parameter[] { new Parameter(ClassHelper.STRING_TYPE.makeArray(), "args")}, + ClassNode.EMPTY_ARRAY, + new ExpressionStatement( + new MethodCallExpression( + new ClassExpression(ClassHelper.make(InvokerHelper.class)), + "runScript", + new ArgumentListExpression( + new ClassExpression(classNode), + new VariableExpression("args")))))); + + MethodNode methodNode = new MethodNode("run", ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, statementBlock); + methodNode.setIsScriptBody(); + classNode.addMethod(methodNode); + + classNode.addConstructor(ACC_PUBLIC, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new BlockStatement()); + + Statement stmt; + // A script's contextual constructor should call it's super class' contextual constructor, if it has one. + // In practice this will always be true because currently this visitor is run before the AST transformations + // (like @BaseScript) that could change this. But this is cautious and anticipates possible compiler changes. + if (classNode.getSuperClass().getDeclaredConstructor(SCRIPT_CONTEXT_CTOR) != null) { + stmt = new ExpressionStatement( + new ConstructorCallExpression(ClassNode.SUPER, + new ArgumentListExpression( + new VariableExpression("context")))); + } else { + // Fallback for non-standard base "script" classes with no context (Binding) constructor. + stmt = new ExpressionStatement( + new MethodCallExpression( + new VariableExpression("super"), + "setBinding", + new ArgumentListExpression( + new VariableExpression("context")))); + } + + classNode.addConstructor( + ACC_PUBLIC, + new Parameter[] { new Parameter(ClassHelper.make(Binding.class), "context")}, + ClassNode.EMPTY_ARRAY, + stmt); + + for (MethodNode node : methods) { + int modifiers = node.getModifiers(); + if ((modifiers & ACC_ABSTRACT) != 0) { + throw new RuntimeException( + "Cannot use abstract methods in a script, they are only available inside classes. Method: " + + node.getName()); + } + // br: the old logic seems to add static to all def f().... in a script, which makes enclosing + // inner classes (including closures) in a def function difficult. Comment it out. + node.setModifiers(modifiers /*| ACC_STATIC*/); + + classNode.addMethod(node); + } + return classNode; + } + + /* + * If a main method is provided by user, account for it under run() as scripts generate their own 'main' so they can run. + */ + private void handleMainMethodIfPresent(List methods) { + boolean found = false; + for (Iterator iter = methods.iterator(); iter.hasNext();) { + MethodNode node = (MethodNode) iter.next(); + if(node.getName().equals("main")) { + if (node.isStatic() && node.getParameters().length == 1) { + boolean retTypeMatches, argTypeMatches; + ClassNode argType = node.getParameters()[0].getType(); + ClassNode retType = node.getReturnType(); + + argTypeMatches = (argType.equals(ClassHelper.OBJECT_TYPE) || argType.getName().contains("String[]")); + retTypeMatches = (retType == ClassHelper.VOID_TYPE || retType == ClassHelper.OBJECT_TYPE); + + if(retTypeMatches && argTypeMatches) { + if(found) { + throw new RuntimeException("Repetitive main method found."); + } else { + found = true; + } + // if script has both loose statements as well as main(), then main() is ignored + if(statementBlock.isEmpty()) { + addStatement(node.getCode()); + } + iter.remove(); + } + } + } + } + } + + protected String extractClassFromFileDescription() { + String answer = getDescription(); + try { + URI uri = new URI(answer); + String path = uri.getPath(); + String schemeSpecific = uri.getSchemeSpecificPart(); + if (path!=null) { + answer = path; + } else if (schemeSpecific!=null) { + answer = schemeSpecific; + } + } catch (URISyntaxException e) {} + // let's strip off everything after the last '.' + int slashIdx = answer.lastIndexOf('/'); + int separatorIdx = answer.lastIndexOf(File.separatorChar); + int dotIdx = answer.lastIndexOf('.'); + if (dotIdx > 0 && dotIdx > Math.max(slashIdx, separatorIdx)) { + answer = answer.substring(0, dotIdx); + } + // new let's strip everything up to and including the path separators + if (slashIdx >= 0) { + answer = answer.substring(slashIdx + 1); + } + // recalculate in case we have already done some stripping + separatorIdx = answer.lastIndexOf(File.separatorChar); + if (separatorIdx >= 0) { + answer = answer.substring(separatorIdx + 1); + } + return answer; + } + + public boolean isEmpty() { + return classes.isEmpty() && statementBlock.getStatements().isEmpty(); + } + + public void sortClasses(){ + if (isEmpty()) return; + List<ClassNode> classes = getClasses(); + LinkedList<ClassNode> sorted = new LinkedList<ClassNode>(); + int level=1; + while (!classes.isEmpty()) { + for (Iterator<ClassNode> cni = classes.iterator(); cni.hasNext();) { + ClassNode cn = cni.next(); + ClassNode sn = cn; + for (int i=0; sn!=null && i<level; i++) sn = sn.getSuperClass(); + if (sn!=null && sn.isPrimaryClassNode()) continue; + cni.remove(); + sorted.addLast(cn); + } + level++; + } + this.classes = sorted; + } + + public boolean hasImportsResolved() { + return importsResolved; + } + + public void setImportsResolved(boolean importsResolved) { + this.importsResolved = importsResolved; + } + + public Map<String, ImportNode> getStaticImports() { + return staticImports; + } + + public Map<String, ImportNode> getStaticStarImports() { + return staticStarImports; + } + + public void addStaticImport(ClassNode type, String fieldName, String alias) { + addStaticImport(type, fieldName, alias, new ArrayList<AnnotationNode>()); + } + + public void addStaticImport(ClassNode type, String fieldName, String alias, List<AnnotationNode> annotations) { + ImportNode node = new ImportNode(type, fieldName, alias); + node.addAnnotations(annotations); + staticImports.put(alias, node); + storeLastAddedImportNode(node); + } + + public void addStaticStarImport(String name, ClassNode type) { + addStaticStarImport(name, type, new ArrayList<AnnotationNode>()); + } + + public void addStaticStarImport(String name, ClassNode type, List<AnnotationNode> annotations) { + ImportNode node = new ImportNode(type); + node.addAnnotations(annotations); + staticStarImports.put(name, node); + storeLastAddedImportNode(node); + } + + // This method only exists as a workaround for GROOVY-6094 + // In order to keep binary compatibility + private void storeLastAddedImportNode(final ImportNode node) { + if (getNodeMetaData(ImportNode.class)==ImportNode.class) { + putNodeMetaData(ImportNode.class, node); + } + } + + public String getMainClassName() { + return mainClassName; + } +}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandler.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandler.java b/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandler.java new file mode 100644 index 0000000..74197a9 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandler.java @@ -0,0 +1,82 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.GroovyBugError; + +import java.util.Map; + +/** + * An interface to mark a node being able to handle metadata + */ +public interface NodeMetaDataHandler { + /** + * Gets the node meta data. + * + * @param key - the meta data key + * @return the node meta data value for this key + */ + <T> T getNodeMetaData(Object key); + + /** + * Copies all node meta data from the other node to this one + * + * @param other - the other node + */ + void copyNodeMetaData(NodeMetaDataHandler other); + + /** + * Sets the node meta data. + * + * @param key - the meta data key + * @param value - the meta data value + * @throws GroovyBugError if key is null or there is already meta + * data under that key + */ + void setNodeMetaData(Object key, Object value); + + /** + * Sets the node meta data but allows overwriting values. + * + * @param key - the meta data key + * @param value - the meta data value + * @return the old node meta data value for this key + * @throws GroovyBugError if key is null + */ + Object putNodeMetaData(Object key, Object value); + + /** + * Removes a node meta data entry. + * + * @param key - the meta data key + * @throws GroovyBugError if the key is null + */ + void removeNodeMetaData(Object key); + + /** + * Returns an unmodifiable view of the current node metadata. + * + * @return the node metadata. Always not null. + */ + Map<?, ?> getNodeMetaData(); + + Map<?, ?> getMetaDataMap(); + + void setMetaDataMap(Map<?, ?> metaDataMap); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandlerHelper.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandlerHelper.java b/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandlerHelper.java new file mode 100644 index 0000000..1287d3c --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/NodeMetaDataHandlerHelper.java @@ -0,0 +1,138 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.util.ListHashMap; + +import java.util.Collections; +import java.util.Map; + +public class NodeMetaDataHandlerHelper { + + private NodeMetaDataHandler delegate; + + public NodeMetaDataHandlerHelper(NodeMetaDataHandler delegate) { + this.delegate = delegate; + } + + /** + * Gets the node meta data. + * + * @param key - the meta data key + * @return the node meta data value for this key + */ + public <T> T getNodeMetaData(Object key) { + Map<?, ?> metaDataMap = delegate.getMetaDataMap(); + + if (metaDataMap == null) { + return null; + } + return (T) metaDataMap.get(key); + } + + /** + * Copies all node meta data from the other node to this one + * + * @param other - the other node + */ + public void copyNodeMetaData(NodeMetaDataHandler other) { + Map otherMetaDataMap = other.getMetaDataMap(); + if (otherMetaDataMap == null) { + return; + } + Map metaDataMap = delegate.getMetaDataMap(); + if (metaDataMap == null) { + metaDataMap = new ListHashMap(); + delegate.setMetaDataMap(metaDataMap); + } + + metaDataMap.putAll(otherMetaDataMap); + } + + /** + * Sets the node meta data. + * + * @param key - the meta data key + * @param value - the meta data value + * @throws GroovyBugError if key is null or there is already meta + * data under that key + */ + public void setNodeMetaData(Object key, Object value) { + if (key == null) throw new GroovyBugError("Tried to set meta data with null key on " + this + "."); + + Map metaDataMap = delegate.getMetaDataMap(); + if (metaDataMap == null) { + metaDataMap = new ListHashMap(); + delegate.setMetaDataMap(metaDataMap); + } + Object old = metaDataMap.put(key, value); + if (old != null) throw new GroovyBugError("Tried to overwrite existing meta data " + this + "."); + } + + /** + * Sets the node meta data but allows overwriting values. + * + * @param key - the meta data key + * @param value - the meta data value + * @return the old node meta data value for this key + * @throws GroovyBugError if key is null + */ + public Object putNodeMetaData(Object key, Object value) { + if (key == null) throw new GroovyBugError("Tried to set meta data with null key on " + this + "."); + + Map metaDataMap = delegate.getMetaDataMap(); + if (metaDataMap == null) { + metaDataMap = new ListHashMap(); + delegate.setMetaDataMap(metaDataMap); + } + return metaDataMap.put(key, value); + } + + /** + * Removes a node meta data entry. + * + * @param key - the meta data key + * @throws GroovyBugError if the key is null + */ + public void removeNodeMetaData(Object key) { + if (key == null) throw new GroovyBugError("Tried to remove meta data with null key " + this + "."); + + Map metaDataMap = delegate.getMetaDataMap(); + if (metaDataMap == null) { + return; + } + metaDataMap.remove(key); + } + + /** + * Returns an unmodifiable view of the current node metadata. + * + * @return the node metadata. Always not null. + */ + public Map<?, ?> getNodeMetaData() { + Map metaDataMap = delegate.getMetaDataMap(); + + if (metaDataMap == null) { + return Collections.emptyMap(); + } + return Collections.unmodifiableMap(metaDataMap); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/PackageNode.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/PackageNode.java b/src/main/java/org/codehaus/groovy/ast/PackageNode.java new file mode 100644 index 0000000..946f9bd --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/PackageNode.java @@ -0,0 +1,46 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +/** + * Represents a package in the AST. + * + * @author Paul King + */ +public class PackageNode extends AnnotatedNode { + private final String name; + + public PackageNode(String name) { + this.name = name; + } + + public String getName() { + return name; + } + + /** + * @return the text display of this package definition + */ + public String getText() { + return "package " + name; + } + + public void visit(GroovyCodeVisitor visitor) { + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/Parameter.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/Parameter.java b/src/main/java/org/codehaus/groovy/ast/Parameter.java new file mode 100644 index 0000000..1b22128 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/Parameter.java @@ -0,0 +1,126 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.ast.expr.Expression; + + +/** + * Represents a parameter on a constructor or method call. The type name is + * optional - it defaults to java.lang.Object if unknown. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class Parameter extends AnnotatedNode implements Variable { + + public static final Parameter[] EMPTY_ARRAY = {}; + + private ClassNode type; + private final String name; + private boolean dynamicTyped; + private Expression defaultValue; + private boolean hasDefaultValue; + private boolean inStaticContext; + private boolean closureShare=false; + private int modifiers; + private ClassNode originType=ClassHelper.DYNAMIC_TYPE; + + public Parameter(ClassNode type, String name) { + this.name = name; + this.setType(type); + this.originType = type; + this.hasDefaultValue = false; + } + + public Parameter(ClassNode type, String name, Expression defaultValue) { + this(type,name); + this.defaultValue = defaultValue; + this.hasDefaultValue = defaultValue != null; + } + + public String toString() { + return super.toString() + "[name:" + name + ((type == null) ? "" : " type: " + type.getName()) + ", hasDefaultValue: " + this.hasInitialExpression() + "]"; + } + + public String getName() { + return name; + } + + public ClassNode getType() { + return type; + } + + public void setType(ClassNode type) { + this.type = type; + dynamicTyped |= type==ClassHelper.DYNAMIC_TYPE; + } + + public boolean hasInitialExpression() { + return this.hasDefaultValue; + } + + /** + * @return the default value expression for this parameter or null if + * no default value is specified + */ + public Expression getInitialExpression() { + return defaultValue; + } + + public void setInitialExpression(Expression init) { + defaultValue = init; + hasDefaultValue = defaultValue != null; + } + + public boolean isInStaticContext() { + return inStaticContext; + } + + public void setInStaticContext(boolean inStaticContext) { + this.inStaticContext = inStaticContext; + } + + public boolean isDynamicTyped() { + return dynamicTyped; + } + + public boolean isClosureSharedVariable() { + return closureShare; + } + + public void setClosureSharedVariable(boolean inClosure) { + closureShare = inClosure; + } + + public int getModifiers() { + return modifiers; + } + + public ClassNode getOriginType() { + return originType; + } + + public void setOriginType(ClassNode cn) { + originType = cn; + } + + public void setModifiers(int modifiers) { + this.modifiers = modifiers; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/PropertyNode.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/PropertyNode.java b/src/main/java/org/codehaus/groovy/ast/PropertyNode.java new file mode 100644 index 0000000..49b941f --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/PropertyNode.java @@ -0,0 +1,135 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.stmt.Statement; +import org.objectweb.asm.Opcodes; + +/** + * Represents a property (member variable, a getter and setter) + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class PropertyNode extends AnnotatedNode implements Opcodes, Variable { + + private FieldNode field; + + private Statement getterBlock; + private Statement setterBlock; + private final int modifiers; + + public PropertyNode( + String name, int modifiers, ClassNode type, ClassNode owner, + Expression initialValueExpression, Statement getterBlock, + Statement setterBlock) { + this(new FieldNode(name, modifiers & ACC_STATIC, type, owner, initialValueExpression), modifiers, getterBlock, setterBlock); + } + + public PropertyNode(FieldNode field, int modifiers, Statement getterBlock, Statement setterBlock) { + this.field = field; + this.modifiers = modifiers; + this.getterBlock = getterBlock; + this.setterBlock = setterBlock; + } + + public Statement getGetterBlock() { + return getterBlock; + } + + public Expression getInitialExpression() { + return field.getInitialExpression(); + } + + public void setGetterBlock(Statement getterBlock) { + this.getterBlock = getterBlock; + } + + public void setSetterBlock(Statement setterBlock) { + this.setterBlock = setterBlock; + } + + public int getModifiers() { + return modifiers; + } + + public String getName() { + return field.getName(); + } + + public Statement getSetterBlock() { + return setterBlock; + } + + public ClassNode getType() { + return field.getType(); + } + + public void setType(ClassNode t) { + field.setType(t); + } + + public FieldNode getField() { + return field; + } + + public void setField(FieldNode fn) { + field = fn; + } + + public boolean isPrivate() { + return (modifiers & ACC_PRIVATE) != 0; + } + + public boolean isPublic() { + return (modifiers & ACC_PUBLIC) != 0; + } + + public boolean isStatic() { + return (modifiers & ACC_STATIC) != 0; + } + + public boolean hasInitialExpression() { + return field.hasInitialExpression(); + } + + public boolean isInStaticContext() { + return field.isInStaticContext(); + } + + public boolean isDynamicTyped() { + return field.isDynamicTyped(); + } + + public boolean isClosureSharedVariable() { + return false; + } + + /** + * @deprecated not used anymore, has no effect + */ + @Deprecated + public void setClosureSharedVariable(boolean inClosure) { + // unused + } + + public ClassNode getOriginType() { + return getType(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/TransformingCodeVisitor.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/TransformingCodeVisitor.java b/src/main/java/org/codehaus/groovy/ast/TransformingCodeVisitor.java new file mode 100644 index 0000000..f3d6dbe --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/TransformingCodeVisitor.java @@ -0,0 +1,371 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ArrayExpression; +import org.codehaus.groovy.ast.expr.AttributeExpression; +import org.codehaus.groovy.ast.expr.BinaryExpression; +import org.codehaus.groovy.ast.expr.BitwiseNegationExpression; +import org.codehaus.groovy.ast.expr.BooleanExpression; +import org.codehaus.groovy.ast.expr.CastExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ClosureListExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.DeclarationExpression; +import org.codehaus.groovy.ast.expr.ElvisOperatorExpression; +import org.codehaus.groovy.ast.expr.FieldExpression; +import org.codehaus.groovy.ast.expr.GStringExpression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.MapEntryExpression; +import org.codehaus.groovy.ast.expr.MapExpression; +import org.codehaus.groovy.ast.expr.MethodPointerExpression; +import org.codehaus.groovy.ast.expr.NotExpression; +import org.codehaus.groovy.ast.expr.PostfixExpression; +import org.codehaus.groovy.ast.expr.PrefixExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.ast.expr.RangeExpression; +import org.codehaus.groovy.ast.expr.SpreadExpression; +import org.codehaus.groovy.ast.expr.SpreadMapExpression; +import org.codehaus.groovy.ast.expr.StaticMethodCallExpression; +import org.codehaus.groovy.ast.expr.TernaryExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.ast.expr.UnaryMinusExpression; +import org.codehaus.groovy.ast.expr.UnaryPlusExpression; +import org.codehaus.groovy.ast.expr.VariableExpression; +import org.codehaus.groovy.ast.stmt.AssertStatement; +import org.codehaus.groovy.ast.stmt.BlockStatement; +import org.codehaus.groovy.ast.stmt.BreakStatement; +import org.codehaus.groovy.ast.stmt.CaseStatement; +import org.codehaus.groovy.ast.stmt.CatchStatement; +import org.codehaus.groovy.ast.stmt.ContinueStatement; +import org.codehaus.groovy.ast.stmt.DoWhileStatement; +import org.codehaus.groovy.ast.stmt.ExpressionStatement; +import org.codehaus.groovy.ast.stmt.ForStatement; +import org.codehaus.groovy.ast.stmt.IfStatement; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.codehaus.groovy.ast.stmt.SwitchStatement; +import org.codehaus.groovy.ast.stmt.SynchronizedStatement; +import org.codehaus.groovy.ast.stmt.ThrowStatement; +import org.codehaus.groovy.ast.stmt.TryCatchStatement; +import org.codehaus.groovy.ast.stmt.WhileStatement; +import org.codehaus.groovy.classgen.BytecodeExpression; + +public class TransformingCodeVisitor extends CodeVisitorSupport { + private final ClassCodeExpressionTransformer trn; + + public TransformingCodeVisitor(final ClassCodeExpressionTransformer trn) { + this.trn = trn; + } + + @Override + public void visitBlockStatement(final BlockStatement block) { + super.visitBlockStatement(block); + trn.visitBlockStatement(block); + } + + @Override + public void visitForLoop(final ForStatement forLoop) { + super.visitForLoop(forLoop); + trn.visitForLoop(forLoop); + } + + @Override + public void visitWhileLoop(final WhileStatement loop) { + super.visitWhileLoop(loop); + trn.visitWhileLoop(loop); + } + + @Override + public void visitDoWhileLoop(final DoWhileStatement loop) { + super.visitDoWhileLoop(loop); + trn.visitDoWhileLoop(loop); + } + + @Override + public void visitIfElse(final IfStatement ifElse) { + super.visitIfElse(ifElse); + trn.visitIfElse(ifElse); + } + + @Override + public void visitExpressionStatement(final ExpressionStatement statement) { + super.visitExpressionStatement(statement); + trn.visitExpressionStatement(statement); + } + + @Override + public void visitReturnStatement(final ReturnStatement statement) { + super.visitReturnStatement(statement); + trn.visitReturnStatement(statement); + } + + @Override + public void visitAssertStatement(final AssertStatement statement) { + super.visitAssertStatement(statement); + trn.visitAssertStatement(statement); + } + + @Override + public void visitTryCatchFinally(final TryCatchStatement statement) { + super.visitTryCatchFinally(statement); + trn.visitTryCatchFinally(statement); + } + + @Override + public void visitSwitch(final SwitchStatement statement) { + super.visitSwitch(statement); + trn.visitSwitch(statement); + } + + @Override + public void visitCaseStatement(final CaseStatement statement) { + super.visitCaseStatement(statement); + trn.visitCaseStatement(statement); + } + + @Override + public void visitBreakStatement(final BreakStatement statement) { + super.visitBreakStatement(statement); + trn.visitBreakStatement(statement); + } + + @Override + public void visitContinueStatement(final ContinueStatement statement) { + super.visitContinueStatement(statement); + trn.visitContinueStatement(statement); + } + + @Override + public void visitSynchronizedStatement(final SynchronizedStatement statement) { + super.visitSynchronizedStatement(statement); + trn.visitSynchronizedStatement(statement); + } + + @Override + public void visitThrowStatement(final ThrowStatement statement) { + super.visitThrowStatement(statement); + trn.visitThrowStatement(statement); + } + + @Override + public void visitStaticMethodCallExpression(final StaticMethodCallExpression call) { + super.visitStaticMethodCallExpression(call); + trn.visitStaticMethodCallExpression(call); + } + + @Override + public void visitBinaryExpression(final BinaryExpression expression) { + super.visitBinaryExpression(expression); + trn.visitBinaryExpression(expression); + } + + @Override + public void visitTernaryExpression(final TernaryExpression expression) { + super.visitTernaryExpression(expression); + trn.visitTernaryExpression(expression); + } + + @Override + public void visitShortTernaryExpression(final ElvisOperatorExpression expression) { + super.visitShortTernaryExpression(expression); + trn.visitShortTernaryExpression(expression); + } + + @Override + public void visitPostfixExpression(final PostfixExpression expression) { + super.visitPostfixExpression(expression); + trn.visitPostfixExpression(expression); + } + + @Override + public void visitPrefixExpression(final PrefixExpression expression) { + super.visitPrefixExpression(expression); + trn.visitPrefixExpression(expression); + } + + @Override + public void visitBooleanExpression(final BooleanExpression expression) { + super.visitBooleanExpression(expression); + trn.visitBooleanExpression(expression); + } + + @Override + public void visitNotExpression(final NotExpression expression) { + super.visitNotExpression(expression); + trn.visitNotExpression(expression); + } + + @Override + public void visitClosureExpression(final ClosureExpression expression) { + super.visitClosureExpression(expression); + trn.visitClosureExpression(expression); + } + + @Override + public void visitTupleExpression(final TupleExpression expression) { + super.visitTupleExpression(expression); + trn.visitTupleExpression(expression); + } + + @Override + public void visitListExpression(final ListExpression expression) { + super.visitListExpression(expression); + trn.visitListExpression(expression); + } + + @Override + public void visitArrayExpression(final ArrayExpression expression) { + super.visitArrayExpression(expression); + trn.visitArrayExpression(expression); + } + + @Override + public void visitMapExpression(final MapExpression expression) { + super.visitMapExpression(expression); + trn.visitMapExpression(expression); + } + + @Override + public void visitMapEntryExpression(final MapEntryExpression expression) { + super.visitMapEntryExpression(expression); + trn.visitMapEntryExpression(expression); + } + + @Override + public void visitRangeExpression(final RangeExpression expression) { + super.visitRangeExpression(expression); + trn.visitRangeExpression(expression); + } + + @Override + public void visitSpreadExpression(final SpreadExpression expression) { + super.visitSpreadExpression(expression); + trn.visitSpreadExpression(expression); + } + + @Override + public void visitSpreadMapExpression(final SpreadMapExpression expression) { + super.visitSpreadMapExpression(expression); + trn.visitSpreadMapExpression(expression); + } + + @Override + public void visitMethodPointerExpression(final MethodPointerExpression expression) { + super.visitMethodPointerExpression(expression); + trn.visitMethodPointerExpression(expression); + } + + @Override + public void visitUnaryMinusExpression(final UnaryMinusExpression expression) { + super.visitUnaryMinusExpression(expression); + trn.visitUnaryMinusExpression(expression); + } + + @Override + public void visitUnaryPlusExpression(final UnaryPlusExpression expression) { + super.visitUnaryPlusExpression(expression); + trn.visitUnaryPlusExpression(expression); + } + + @Override + public void visitBitwiseNegationExpression(final BitwiseNegationExpression expression) { + super.visitBitwiseNegationExpression(expression); + trn.visitBitwiseNegationExpression(expression); + } + + @Override + public void visitCastExpression(final CastExpression expression) { + super.visitCastExpression(expression); + trn.visitCastExpression(expression); + } + + @Override + public void visitConstantExpression(final ConstantExpression expression) { + super.visitConstantExpression(expression); + trn.visitConstantExpression(expression); + } + + @Override + public void visitClassExpression(final ClassExpression expression) { + super.visitClassExpression(expression); + trn.visitClassExpression(expression); + } + + @Override + public void visitVariableExpression(final VariableExpression expression) { + super.visitVariableExpression(expression); + trn.visitVariableExpression(expression); + } + + @Override + public void visitDeclarationExpression(final DeclarationExpression expression) { + super.visitDeclarationExpression(expression); + trn.visitDeclarationExpression(expression); + } + + @Override + public void visitPropertyExpression(final PropertyExpression expression) { + super.visitPropertyExpression(expression); + trn.visitPropertyExpression(expression); + } + + @Override + public void visitAttributeExpression(final AttributeExpression expression) { + super.visitAttributeExpression(expression); + trn.visitAttributeExpression(expression); + } + + @Override + public void visitFieldExpression(final FieldExpression expression) { + super.visitFieldExpression(expression); + trn.visitFieldExpression(expression); + } + + @Override + public void visitGStringExpression(final GStringExpression expression) { + super.visitGStringExpression(expression); + trn.visitGStringExpression(expression); + } + + @Override + public void visitCatchStatement(final CatchStatement statement) { + super.visitCatchStatement(statement); + trn.visitCatchStatement(statement); + } + + @Override + public void visitArgumentlistExpression(final ArgumentListExpression ale) { + super.visitArgumentlistExpression(ale); + trn.visitArgumentlistExpression(ale); + } + + @Override + public void visitClosureListExpression(final ClosureListExpression cle) { + super.visitClosureListExpression(cle); + trn.visitClosureListExpression(cle); + } + + @Override + public void visitBytecodeExpression(final BytecodeExpression cle) { + super.visitBytecodeExpression(cle); + trn.visitBytecodeExpression(cle); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/Variable.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/Variable.java b/src/main/java/org/codehaus/groovy/ast/Variable.java new file mode 100644 index 0000000..2e94739 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/Variable.java @@ -0,0 +1,69 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import org.codehaus.groovy.ast.expr.Expression; + +/** + * interface to mark a AstNode as Variable. Typically these are + * VariableExpression, FieldNode, PropertyNode and Parameter + * + * @author Jochen Theodorou + */ +public interface Variable { + + /** + * the type of the variable + */ + ClassNode getType(); + + /** + * the type before wrapping primitives type of the variable + */ + ClassNode getOriginType(); + + /** + * the name of the variable + */ + String getName(); + + /** + * expression used to initialize the variable or null of there + * is no initialization. + */ + Expression getInitialExpression(); + + /** + * returns true if there is an initialization expression + */ + boolean hasInitialExpression(); + + /** + * returns true if this variable is used in a static context. + * A static context is any static initializer block, when this variable + * is declared as static or when this variable is used in a static method + */ + boolean isInStaticContext(); + + boolean isDynamicTyped(); + boolean isClosureSharedVariable(); + void setClosureSharedVariable(boolean inClosure); + + int getModifiers(); +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/VariableScope.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/VariableScope.java b/src/main/java/org/codehaus/groovy/ast/VariableScope.java new file mode 100644 index 0000000..521b301 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/VariableScope.java @@ -0,0 +1,200 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast; + +import java.util.Collections; +import java.util.Iterator; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * Represents a variable scope. This is primarily used to determine variable sharing + * across method and closure boundaries. + * + * @author <a href="mailto:[email protected]">James Strachan</a> + * @author Jochen Theodorou + */ +public class VariableScope { + private Map<String, Variable> declaredVariables = Collections.emptyMap(); + private Map<String, Variable> referencedLocalVariables = Collections.emptyMap(); + private Map<String, Variable> referencedClassVariables = Collections.emptyMap(); + + private boolean inStaticContext = false; + private boolean resolvesDynamic = false; + // Non-null iff this scope corresponds to a class, as opposed to a method, "if" statement, + // block statement, etc. + private ClassNode clazzScope; + private VariableScope parent; + + public VariableScope() { + } + public VariableScope(VariableScope parent) { + this.parent = parent; + } + + public Variable getDeclaredVariable(String name) { + return declaredVariables.get(name); + } + + public boolean isReferencedLocalVariable(String name) { + return referencedLocalVariables.containsKey(name); + } + + public boolean isReferencedClassVariable(String name) { + return referencedClassVariables.containsKey(name); + } + public VariableScope getParent() { + return parent; + } + + public boolean isInStaticContext() { + return inStaticContext; + } + + public void setInStaticContext(boolean inStaticContext) { + this.inStaticContext = inStaticContext; + } + + public void setClassScope(ClassNode node) { + this.clazzScope = node; + } + + /** + * Non-null iff this scope corresponds to a class; as opposed to a method, "if" statement, + * block statement, etc. + */ + public ClassNode getClassScope(){ + return clazzScope; + } + + /** + * Returns true iff this scope corresponds to a class; as opposed to a method, "if" statement, + * block statement, etc. + */ + public boolean isClassScope(){ + return clazzScope!=null; + } + + public boolean isRoot() { + return parent==null; + } + + public VariableScope copy() { + VariableScope copy = new VariableScope(); + copy.clazzScope = clazzScope; + if (!declaredVariables.isEmpty()) { + copy.declaredVariables = new LinkedHashMap<String, Variable>(declaredVariables); + } + copy.inStaticContext = inStaticContext; + copy.parent = parent; + if (!referencedClassVariables.isEmpty()) { + copy.referencedClassVariables = new LinkedHashMap<String, Variable>(referencedClassVariables); + } + if (!referencedLocalVariables.isEmpty()) { + copy.referencedLocalVariables = new LinkedHashMap<String, Variable>(referencedLocalVariables); + } + copy.resolvesDynamic = resolvesDynamic; + return copy; + } + + public void putDeclaredVariable(Variable var) { + if (declaredVariables == Collections.EMPTY_MAP) + declaredVariables = new LinkedHashMap<String, Variable>(); + declaredVariables.put(var.getName(), var); + } + + public Iterator<Variable> getReferencedLocalVariablesIterator() { + return referencedLocalVariables.values().iterator(); + } + + public int getReferencedLocalVariablesCount() { + return referencedLocalVariables.size(); + } + + public Variable getReferencedLocalVariable(String name) { + return referencedLocalVariables.get(name); + } + + public void putReferencedLocalVariable(Variable var) { + if (referencedLocalVariables == Collections.EMPTY_MAP) + referencedLocalVariables = new LinkedHashMap<String, Variable>(); + referencedLocalVariables.put(var.getName(), var); + } + + public void putReferencedClassVariable(Variable var) { + if (referencedClassVariables == Collections.EMPTY_MAP) + referencedClassVariables = new LinkedHashMap<String, Variable>(); + referencedClassVariables.put(var.getName(), var); + } + + public Variable getReferencedClassVariable(String name) { + return referencedClassVariables.get(name); + } + + public Object removeReferencedClassVariable(String name) { + if (referencedClassVariables == Collections.EMPTY_MAP) + return null; + else + return referencedClassVariables.remove(name); + } + + /** + * Gets a map containing the class variables referenced + * by this scope. This not can not be modified. + * @return a map containing the class variable references + */ + public Map<String, Variable> getReferencedClassVariables() { + if (referencedClassVariables == Collections.EMPTY_MAP) { + return referencedClassVariables; + } else { + return Collections.unmodifiableMap(referencedClassVariables); + } + } + + /** + * Gets an iterator for the referenced class variables. The + * remove operation is not supported. + * @return an iterator for the referenced class variables + */ + public Iterator<Variable> getReferencedClassVariablesIterator() { + return getReferencedClassVariables().values().iterator(); + } + + /** + * Gets a map containing the variables declared in this scope. + * This map cannot be modified. + * @return a map containing the declared variable references + */ + public Map<String, Variable> getDeclaredVariables() { + if (declaredVariables == Collections.EMPTY_MAP) { + return declaredVariables; + } else { + return Collections.unmodifiableMap(declaredVariables); + } + } + + /** + * Gets an iterator for the declared class variables. The remove + * operation is not supported. + * @return an iterator for the declared variables + */ + public Iterator<Variable> getDeclaredVariablesIterator() { + return getDeclaredVariables().values().iterator(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java b/src/main/java/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java new file mode 100644 index 0000000..6882498 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/builder/AstBuilderTransformation.java @@ -0,0 +1,186 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast.builder; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.ImportNode; +import org.codehaus.groovy.ast.MethodCallTransformation; +import org.codehaus.groovy.ast.MethodInvocationTrap; +import org.codehaus.groovy.ast.expr.ArgumentListExpression; +import org.codehaus.groovy.ast.expr.ClosureExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.MethodCallExpression; +import org.codehaus.groovy.ast.expr.TupleExpression; +import org.codehaus.groovy.control.CompilePhase; +import org.codehaus.groovy.control.SourceUnit; +import org.codehaus.groovy.control.io.ReaderSource; +import org.codehaus.groovy.transform.GroovyASTTransformation; + +import java.util.ArrayList; +import java.util.List; + +/** + * Transformation to capture ASTBuilder from code statements. + * <p> + * The AstBuilder "from code" approach is used with a single Closure + * parameter. This transformation converts the ClosureExpression back + * into source code and rewrites the AST so that the "from string" + * builder is invoked on the source. In order for this to work, the + * closure source must be given a goto label. It is the "from string" + * approach's responsibility to remove the BlockStatement created + * by the label. + * + * @author Hamlet D'Arcy + */ + +@GroovyASTTransformation(phase = CompilePhase.SEMANTIC_ANALYSIS) +public class AstBuilderTransformation extends MethodCallTransformation { + + @Override + protected GroovyCodeVisitor getTransformer(ASTNode[] nodes, SourceUnit sourceUnit) { + // todo : are there other import types that can be specified? + return new AstBuilderInvocationTrap( + sourceUnit.getAST().getImports(), + sourceUnit.getAST().getStarImports(), + sourceUnit.getSource(), + sourceUnit + ); + } + + /** + * This class traps invocations of AstBuilder.build(CompilePhase, boolean, Closure) and converts + * the contents of the closure into expressions by reading the source of the Closure and sending + * that as a String to AstBuilder.build(String, CompilePhase, boolean) at runtime. + */ + private static class AstBuilderInvocationTrap extends MethodInvocationTrap { + + private final List<String> factoryTargets = new ArrayList<String>(); + + /** + * Creates the trap and captures all the ways in which a class may be referenced via imports. + * + * @param imports all the imports from the source + * @param importPackages all the imported packages from the source + * @param source the reader source that contains source for the SourceUnit + * @param sourceUnit the source unit being compiled. Used for error messages. + */ + AstBuilderInvocationTrap(List<ImportNode> imports, List<ImportNode> importPackages, ReaderSource source, SourceUnit sourceUnit) { + super(source, sourceUnit); + + // factory type may be references as fully qualified, an import, or an alias + factoryTargets.add("org.codehaus.groovy.ast.builder.AstBuilder");//default package + + if (imports != null) { + for (ImportNode importStatement : imports) { + if ("org.codehaus.groovy.ast.builder.AstBuilder".equals(importStatement.getType().getName())) { + factoryTargets.add(importStatement.getAlias()); + } + } + } + + if (importPackages != null) { + for (ImportNode importPackage : importPackages) { + if ("org.codehaus.groovy.ast.builder.".equals(importPackage.getPackageName())) { + factoryTargets.add("AstBuilder"); + break; + } + } + } + } + + @Override + protected boolean handleTargetMethodCallExpression(MethodCallExpression call) { + ClosureExpression closureExpression = getClosureArgument(call); + List<Expression> otherArgs = getNonClosureArguments(call); + String source = convertClosureToSource(closureExpression); + + // parameter order is build(CompilePhase, boolean, String) + otherArgs.add(new ConstantExpression(source)); + call.setArguments(new ArgumentListExpression(otherArgs)); + call.setMethod(new ConstantExpression("buildFromBlock")); + call.setSpreadSafe(false); + call.setSafe(false); + call.setImplicitThis(false); + + return false; + } + + private static List<Expression> getNonClosureArguments(MethodCallExpression call) { + List<Expression> result = new ArrayList<Expression>(); + if (call.getArguments() instanceof TupleExpression) { + for (ASTNode node : ((TupleExpression) call.getArguments()).getExpressions()) { + if (!(node instanceof ClosureExpression)) { + result.add((Expression) node); + } + } + } + return result; + } + + private static ClosureExpression getClosureArgument(MethodCallExpression call) { + + if (call.getArguments() instanceof TupleExpression) { + for (ASTNode node : ((TupleExpression) call.getArguments()).getExpressions()) { + if (node instanceof ClosureExpression) { + return (ClosureExpression) node; + } + } + } + return null; + } + + /** + * Looks for method calls on the AstBuilder class called build that take + * a Closure as parameter. This is all needed b/c build is overloaded. + * + * @param call the method call expression, may not be null + */ + @Override + protected boolean isBuildInvocation(MethodCallExpression call) { + if (call == null) throw new IllegalArgumentException("Null: call"); + + // is method name correct? + if (call.getMethod() instanceof ConstantExpression && "buildFromCode".equals(((ConstantExpression) call.getMethod()).getValue())) { + + // is method object correct type? + if (call.getObjectExpression() != null && call.getObjectExpression().getType() != null) { + String name = call.getObjectExpression().getType().getName(); + if (name != null && !"".equals(name) && factoryTargets.contains(name)) { + + // is one of the arguments a closure? + if (call.getArguments() != null && call.getArguments() instanceof TupleExpression) { + if (((TupleExpression) call.getArguments()).getExpressions() != null) { + for (ASTNode node : ((TupleExpression) call.getArguments()).getExpressions()) { + if (node instanceof ClosureExpression) { + return true; + } + } + } + } + } + } + } + return false; + } + } +} + + http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java b/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java new file mode 100644 index 0000000..2974d00 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/Annotations.java @@ -0,0 +1,136 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast.decompiled; + +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.expr.AnnotationConstantExpression; +import org.codehaus.groovy.ast.expr.ClassExpression; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.expr.Expression; +import org.codehaus.groovy.ast.expr.ListExpression; +import org.codehaus.groovy.ast.expr.PropertyExpression; +import org.codehaus.groovy.vmplugin.VMPluginFactory; +import org.objectweb.asm.Type; + +import java.lang.reflect.Array; +import java.util.List; +import java.util.Map; + +/** + * @author Peter Gromov + */ +class Annotations { + static AnnotationNode createAnnotationNode(AnnotationStub annotation, AsmReferenceResolver resolver) { + ClassNode classNode = resolver.resolveClassNullable(Type.getType(annotation.className).getClassName()); + if (classNode == null) { + // there might be annotations not present in the classpath + // e.g. java.lang.Synthetic (http://forge.ow2.org/tracker/?aid=307392&group_id=23&atid=100023&func=detail) + // so skip them + return null; + } + + AnnotationNode node = new DecompiledAnnotationNode(classNode); + for (Map.Entry<String, Object> entry : annotation.members.entrySet()) { + node.addMember(entry.getKey(), annotationValueToExpression(entry.getValue(), resolver)); + } + return node; + } + + private static Expression annotationValueToExpression(Object value, AsmReferenceResolver resolver) { + if (value instanceof TypeWrapper) { + return new ClassExpression(resolver.resolveType(Type.getType(((TypeWrapper) value).desc))); + } + + if (value instanceof EnumConstantWrapper) { + EnumConstantWrapper wrapper = (EnumConstantWrapper) value; + return new PropertyExpression(new ClassExpression(resolver.resolveType(Type.getType(wrapper.enumDesc))), wrapper.constant); + } + + if (value instanceof AnnotationStub) { + AnnotationNode annotationNode = createAnnotationNode((AnnotationStub) value, resolver); + return annotationNode != null ? new AnnotationConstantExpression(annotationNode) : ConstantExpression.NULL; + } + + if (value != null && value.getClass().isArray()) { + ListExpression elementExprs = new ListExpression(); + int len = Array.getLength(value); + for (int i = 0; i != len; ++i) { + elementExprs.addExpression(annotationValueToExpression(Array.get(value, i), resolver)); + } + return elementExprs; + } + + if (value instanceof List) { + ListExpression elementExprs = new ListExpression(); + for (Object o : (List) value) { + elementExprs.addExpression(annotationValueToExpression(o, resolver)); + } + return elementExprs; + } + + return new ConstantExpression(value); + } + + private static class DecompiledAnnotationNode extends AnnotationNode { + private final Object initLock; + private volatile boolean lazyInitDone; + + public DecompiledAnnotationNode(ClassNode type) { + super(type); + initLock = new Object(); + } + + private void lazyInit() { + if (lazyInitDone) return; + synchronized (initLock) { + if (!lazyInitDone) { + for (AnnotationNode annotation : getClassNode().getAnnotations()) { + VMPluginFactory.getPlugin().configureAnnotationNodeFromDefinition(annotation, this); + } + lazyInitDone = true; + } + } + } + + @Override + public boolean isTargetAllowed(int target) { + lazyInit(); + return super.isTargetAllowed(target); + } + + @Override + public boolean hasRuntimeRetention() { + lazyInit(); + return super.hasRuntimeRetention(); + } + + @Override + public boolean hasSourceRetention() { + lazyInit(); + return super.hasSourceRetention(); + } + + @Override + public boolean hasClassRetention() { + lazyInit(); + return super.hasClassRetention(); + } + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java new file mode 100644 index 0000000..933f1b5 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmDecompiler.java @@ -0,0 +1,218 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast.decompiled; + +import groovy.lang.GroovyRuntimeException; +import org.objectweb.asm.AnnotationVisitor; +import org.objectweb.asm.ClassReader; +import org.objectweb.asm.ClassVisitor; +import org.objectweb.asm.FieldVisitor; +import org.objectweb.asm.MethodVisitor; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; + +import java.io.BufferedInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.lang.ref.SoftReference; +import java.net.URI; +import java.net.URISyntaxException; +import java.net.URL; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +/** + * A utility class responsible for decompiling JVM class files and producing {@link ClassStub} objects reflecting their structure. + * + * @author Peter Gromov + */ +public abstract class AsmDecompiler { + + private static class StubCache { + /** + * Caches stubs per URI. This cache is useful when performing multiple compilations in the same JVM/class loader and in tests. + * + * It's synchronized "just in case". Occasional misses are expected if several threads attempt to load the same class, + * but this shouldn't result in serious memory issues. + */ + static final Map<URI, SoftReference<ClassStub>> map = new ConcurrentHashMap<URI, SoftReference<ClassStub>>(); // According to http://michaelscharf.blogspot.jp/2006/11/javaneturlequals-and-hashcode-make.html, use java.net.URI instead. + } + + /** + * Loads the URL contents and parses them with ASM, producing a {@link ClassStub} object representing the structure of + * the corresponding class file. Stubs are cached and reused if queried several times with equal URLs. + * + * @param url an URL from a class loader, most likely a file system file or a JAR entry. + * @return the class stub + * @throws IOException if reading from this URL is impossible + */ + public static ClassStub parseClass(URL url) throws IOException { + URI uri; + try { + uri = url.toURI(); + } catch (URISyntaxException e) { + throw new GroovyRuntimeException(e); + } + + SoftReference<ClassStub> ref = StubCache.map.get(uri); + ClassStub stub = ref == null ? null : ref.get(); + if (stub == null) { + DecompilingVisitor visitor = new DecompilingVisitor(); + InputStream stream = url.openStream(); + try { + new ClassReader(new BufferedInputStream(stream)).accept(visitor, ClassReader.SKIP_FRAMES); + } finally { + stream.close(); + } + stub = visitor.result; + StubCache.map.put(uri, new SoftReference<ClassStub>(stub)); + } + return stub; + } + + private static class DecompilingVisitor extends ClassVisitor { + private static final String[] EMPTY_STRING_ARRAY = new String[0]; + private ClassStub result; + + public DecompilingVisitor() { + super(Opcodes.ASM5); + } + + @Override + public void visit(int version, int access, String name, String signature, String superName, String[] interfaceNames) { + result = new ClassStub(fromInternalName(name), access, signature, superName, interfaceNames); + } + + @Override + public void visitInnerClass(String name, String outerName, String innerName, int access) { + result.innerClassModifiers.put(innerName, access); + } + + @Override + public MethodVisitor visitMethod(int access, String name, String desc, String signature, String[] exceptions) { + if (!"<clinit>".equals(name)) { + final MethodStub stub = new MethodStub(name, access, desc, signature, exceptions != null ? exceptions : EMPTY_STRING_ARRAY); + if (result.methods == null) result.methods = new ArrayList<MethodStub>(1); + result.methods.add(stub); + return new MethodVisitor(api) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return readAnnotationMembers(stub.addAnnotation(desc)); + } + + @Override + public AnnotationVisitor visitParameterAnnotation(int parameter, String desc, boolean visible) { + if (stub.parameterAnnotations == null) stub.parameterAnnotations = new HashMap<Integer, List<AnnotationStub>>(1); + List<AnnotationStub> list = stub.parameterAnnotations.get(parameter); + if (list == null) { + stub.parameterAnnotations.put(parameter, list = new ArrayList<AnnotationStub>()); + } + AnnotationStub annotationStub = new AnnotationStub(desc); + list.add(annotationStub); + return readAnnotationMembers(annotationStub); + } + + @Override + public AnnotationVisitor visitAnnotationDefault() { + return new AnnotationReader() { + @Override + void visitAttribute(String name, Object value) { + stub.annotationDefault = value; + } + }; + } + }; + } + return null; + } + + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return readAnnotationMembers(result.addAnnotation(desc)); + } + + @Override + public FieldVisitor visitField(int access, String name, String desc, String signature, Object value) { + final FieldStub stub = new FieldStub(name, access, desc, signature); + if (result.fields == null) result.fields = new ArrayList<FieldStub>(1); + result.fields.add(stub); + return new FieldVisitor(api) { + @Override + public AnnotationVisitor visitAnnotation(String desc, boolean visible) { + return readAnnotationMembers(stub.addAnnotation(desc)); + } + }; + } + } + + private static AnnotationReader readAnnotationMembers(final AnnotationStub stub) { + return new AnnotationReader() { + @Override + void visitAttribute(String name, Object value) { + stub.members.put(name, value); + } + }; + } + + static String fromInternalName(String name) { + return name.replace('/', '.'); + } + + private abstract static class AnnotationReader extends AnnotationVisitor { + public AnnotationReader() { + super(Opcodes.ASM5); + } + + abstract void visitAttribute(String name, Object value); + + @Override + public void visit(String name, Object value) { + visitAttribute(name, value instanceof Type ? new TypeWrapper(((Type) value).getDescriptor()) : value); + } + + @Override + public void visitEnum(String name, String desc, String value) { + visitAttribute(name, new EnumConstantWrapper(desc, value)); + } + + @Override + public AnnotationVisitor visitAnnotation(String name, String desc) { + AnnotationStub stub = new AnnotationStub(desc); + visitAttribute(name, stub); + return readAnnotationMembers(stub); + } + + @Override + public AnnotationVisitor visitArray(String name) { + final List<Object> list = new ArrayList<Object>(); + visitAttribute(name, list); + return new AnnotationReader() { + @Override + void visitAttribute(String name, Object value) { + list.add(value); + } + }; + } + + } +} + http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/AsmReferenceResolver.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/AsmReferenceResolver.java b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmReferenceResolver.java new file mode 100644 index 0000000..9b96fa1 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/AsmReferenceResolver.java @@ -0,0 +1,92 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one + * or more contributor license agreements. See the NOTICE file + * distributed with this work for additional information + * regarding copyright ownership. The ASF licenses this file + * to you under the Apache License, Version 2.0 (the + * "License"); you may not use this file except in compliance + * with the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, + * software distributed under the License is distributed on an + * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY + * KIND, either express or implied. See the License for the + * specific language governing permissions and limitations + * under the License. + */ +package org.codehaus.groovy.ast.decompiled; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.control.ClassNodeResolver; +import org.codehaus.groovy.control.CompilationUnit; +import org.objectweb.asm.Type; + +/** + * A helper class used to resolve references found in ASM-decompiled classes. + * + * @see DecompiledClassNode + * @see AsmDecompiler + * + * @author Peter Gromov + */ +public class AsmReferenceResolver { + private final ClassNodeResolver resolver; + private final CompilationUnit unit; + + public AsmReferenceResolver(ClassNodeResolver resolver, CompilationUnit unit) { + this.resolver = resolver; + this.unit = unit; + } + + public ClassNode resolveClass(String className) { + ClassNode classNode = resolveClassNullable(className); + if (classNode == null) { + throw new NoClassDefFoundError(className); + } + return classNode; + } + + public ClassNode resolveClassNullable(String className) { + ClassNode beingCompiled = unit.getAST().getClass(className); + if (beingCompiled != null) { + return beingCompiled; + } + + ClassNodeResolver.LookupResult lookupResult = resolver.resolveName(className, unit); + return lookupResult == null ? null :lookupResult.getClassNode(); + } + + public ClassNode resolveType(Type type) { + if (type.getSort() == Type.ARRAY) { + ClassNode result = resolveNonArrayType(type.getElementType()); + for (int i = 0; i < type.getDimensions(); i++) { + result = result.makeArray(); + } + return result; + } + + return resolveNonArrayType(type); + } + + private ClassNode resolveNonArrayType(Type type) { + String className = type.getClassName(); + if (type.getSort() != Type.OBJECT) { + return ClassHelper.make(className); + } + + return resolveClass(className); + } + + public Class resolveJvmClass(String name) { + try { + return unit.getClassLoader().loadClass(name, false, true); + } catch (ClassNotFoundException e) { + throw new GroovyBugError("JVM class can't be loaded for " + name, e); + } + } + +}
