You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@groovy.apache.org by cc...@apache.org on 2017/12/17 14:01:46 UTC
[35/62] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
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:james@coredevelopers.net">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:james@coredevelopers.net">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:james@coredevelopers.net">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:james@coredevelopers.net">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);
+ }
+ }
+
+}