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/14 21:56:37 UTC
[12/57] [abbrv] [partial] groovy git commit: Move Java source set
into `src/main/java`
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
new file mode 100644
index 0000000..199b4ad
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/StaticImportVisitor.java
@@ -0,0 +1,610 @@
+/*
+ * 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.control;
+
+import org.codehaus.groovy.ast.AnnotatedNode;
+import org.codehaus.groovy.ast.AnnotationNode;
+import org.codehaus.groovy.ast.ClassCodeExpressionTransformer;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.DynamicVariable;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.ImportNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.AnnotationConstantExpression;
+import org.codehaus.groovy.ast.expr.ArgumentListExpression;
+import org.codehaus.groovy.ast.expr.BinaryExpression;
+import org.codehaus.groovy.ast.expr.ClassExpression;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstantExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.EmptyExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.ListExpression;
+import org.codehaus.groovy.ast.expr.MapEntryExpression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.NamedArgumentListExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
+import org.codehaus.groovy.ast.expr.TupleExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+import org.codehaus.groovy.ast.stmt.Statement;
+import org.codehaus.groovy.syntax.Types;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.getPropNameForAccessor;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticMethod;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasPossibleStaticProperty;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.hasStaticProperty;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isValidAccessorName;
+import static org.codehaus.groovy.runtime.MetaClassHelper.capitalize;
+
+/**
+ * Visitor to resolve constants and method calls from static Imports
+ */
+public class StaticImportVisitor extends ClassCodeExpressionTransformer {
+ private ClassNode currentClass;
+ private MethodNode currentMethod;
+ private SourceUnit source;
+ private boolean inSpecialConstructorCall;
+ private boolean inClosure;
+ private boolean inPropertyExpression;
+ private Expression foundConstant;
+ private Expression foundArgs;
+ private boolean inAnnotation;
+ private boolean inLeftExpression;
+
+ public void visitClass(ClassNode node, SourceUnit source) {
+ this.currentClass = node;
+ this.source = source;
+ super.visitClass(node);
+ }
+
+ @Override
+ protected void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+ this.currentMethod = node;
+ super.visitConstructorOrMethod(node, isConstructor);
+ this.currentMethod = null;
+ }
+
+ @Override
+ public void visitAnnotations(AnnotatedNode node) {
+ boolean oldInAnnotation = inAnnotation;
+ inAnnotation = true;
+ super.visitAnnotations(node);
+ inAnnotation = oldInAnnotation;
+ }
+
+ public Expression transform(Expression exp) {
+ if (exp == null) return null;
+ if (exp.getClass() == VariableExpression.class) {
+ return transformVariableExpression((VariableExpression) exp);
+ }
+ if (exp.getClass() == BinaryExpression.class) {
+ return transformBinaryExpression((BinaryExpression) exp);
+ }
+ if (exp.getClass() == PropertyExpression.class) {
+ return transformPropertyExpression((PropertyExpression) exp);
+ }
+ if (exp.getClass() == MethodCallExpression.class) {
+ return transformMethodCallExpression((MethodCallExpression) exp);
+ }
+ if (exp.getClass() == ClosureExpression.class) {
+ return transformClosureExpression((ClosureExpression) exp);
+ }
+ if (exp.getClass() == ConstructorCallExpression.class) {
+ return transformConstructorCallExpression((ConstructorCallExpression) exp);
+ }
+ if (exp.getClass() == ArgumentListExpression.class) {
+ Expression result = exp.transformExpression(this);
+ if (inPropertyExpression) {
+ foundArgs = result;
+ }
+ return result;
+ }
+ if (exp instanceof ConstantExpression) {
+ Expression result = exp.transformExpression(this);
+ if (inPropertyExpression) {
+ foundConstant = result;
+ }
+ if (inAnnotation && exp instanceof AnnotationConstantExpression) {
+ ConstantExpression ce = (ConstantExpression) result;
+ if (ce.getValue() instanceof AnnotationNode) {
+ // replicate a little bit of AnnotationVisitor here
+ // because we can't wait until later to do this
+ AnnotationNode an = (AnnotationNode) ce.getValue();
+ Map<String, Expression> attributes = an.getMembers();
+ for (Map.Entry<String, Expression> entry : attributes.entrySet()) {
+ Expression attrExpr = transform(entry.getValue());
+ entry.setValue(attrExpr);
+ }
+
+ }
+ }
+ return result;
+ }
+ return exp.transformExpression(this);
+ }
+
+ // if you have a Bar class with a static foo property, and this:
+ // import static Bar.foo as baz
+ // then this constructor (not normal usage of statics):
+ // new Bar(baz:1)
+ // will become:
+ // new Bar(foo:1)
+
+ private Expression transformMapEntryExpression(MapEntryExpression me, ClassNode constructorCallType) {
+ Expression key = me.getKeyExpression();
+ Expression value = me.getValueExpression();
+ ModuleNode module = currentClass.getModule();
+ if (module != null && key instanceof ConstantExpression) {
+ Map<String, ImportNode> importNodes = module.getStaticImports();
+ if (importNodes.containsKey(key.getText())) {
+ ImportNode importNode = importNodes.get(key.getText());
+ if (importNode.getType().equals(constructorCallType)) {
+ String newKey = importNode.getFieldName();
+ return new MapEntryExpression(new ConstantExpression(newKey), value.transformExpression(this));
+ }
+ }
+ }
+ return me;
+ }
+
+ protected Expression transformBinaryExpression(BinaryExpression be) {
+ int type = be.getOperation().getType();
+ boolean oldInLeftExpression;
+ Expression right = transform(be.getRightExpression());
+ be.setRightExpression(right);
+ Expression left;
+ if (type == Types.EQUAL && be.getLeftExpression() instanceof VariableExpression) {
+ oldInLeftExpression = inLeftExpression;
+ inLeftExpression = true;
+ left = transform(be.getLeftExpression());
+ inLeftExpression = oldInLeftExpression;
+ if (left instanceof StaticMethodCallExpression) {
+ StaticMethodCallExpression smce = (StaticMethodCallExpression) left;
+ StaticMethodCallExpression result = new StaticMethodCallExpression(smce.getOwnerType(), smce.getMethod(), right);
+ setSourcePosition(result, be);
+ return result;
+ }
+ } else {
+ left = transform(be.getLeftExpression());
+ }
+ be.setLeftExpression(left);
+ return be;
+ }
+
+ protected Expression transformVariableExpression(VariableExpression ve) {
+ Variable v = ve.getAccessedVariable();
+ if (v != null && v instanceof DynamicVariable) {
+ Expression result = findStaticFieldOrPropAccessorImportFromModule(v.getName());
+ if (result != null) {
+ setSourcePosition(result, ve);
+ if (inAnnotation) {
+ result = transformInlineConstants(result);
+ }
+ return result;
+ }
+ }
+ return ve;
+ }
+
+ /**
+ * Set the source position of toSet including its property expression if it has one.
+ *
+ * @param toSet resulting node
+ * @param origNode original node
+ */
+ private static void setSourcePosition(Expression toSet, Expression origNode) {
+ toSet.setSourcePosition(origNode);
+ if (toSet instanceof PropertyExpression) {
+ ((PropertyExpression) toSet).getProperty().setSourcePosition(origNode);
+ }
+ }
+
+ // resolve constant-looking expressions statically (do here as gets transformed away later)
+
+ private Expression transformInlineConstants(Expression exp) {
+ if (exp instanceof PropertyExpression) {
+ PropertyExpression pe = (PropertyExpression) exp;
+ if (pe.getObjectExpression() instanceof ClassExpression) {
+ ClassExpression ce = (ClassExpression) pe.getObjectExpression();
+ ClassNode type = ce.getType();
+ if (type.isEnum()) return exp;
+ Expression constant = findConstant(getField(type, pe.getPropertyAsString()));
+ if (constant != null) return constant;
+ }
+ } else if (exp instanceof ListExpression) {
+ ListExpression le = (ListExpression) exp;
+ ListExpression result = new ListExpression();
+ for (Expression e : le.getExpressions()) {
+ result.addExpression(transformInlineConstants(e));
+ }
+ return result;
+ }
+
+ return exp;
+ }
+
+ private static Expression findConstant(FieldNode fn) {
+ if (fn != null && !fn.isEnum() && fn.isStatic() && fn.isFinal()) {
+ if (fn.getInitialValueExpression() instanceof ConstantExpression) {
+ return fn.getInitialValueExpression();
+ }
+ }
+ return null;
+ }
+
+ protected Expression transformMethodCallExpression(MethodCallExpression mce) {
+ Expression args = transform(mce.getArguments());
+ Expression method = transform(mce.getMethod());
+ Expression object = transform(mce.getObjectExpression());
+ boolean isExplicitThisOrSuper = false;
+ boolean isExplicitSuper = false;
+ if (object instanceof VariableExpression) {
+ VariableExpression ve = (VariableExpression) object;
+ isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression());
+ isExplicitSuper = ve.isSuperExpression();
+ }
+
+ if (mce.isImplicitThis() || isExplicitThisOrSuper) {
+ if (mce.isImplicitThis()) {
+ Expression ret = findStaticMethodImportFromModule(method, args);
+ if (ret != null) {
+ setSourcePosition(ret, mce);
+ return ret;
+ }
+ if (method instanceof ConstantExpression && !inLeftExpression) {
+ // could be a closure field
+ String methodName = (String) ((ConstantExpression) method).getValue();
+ ret = findStaticFieldOrPropAccessorImportFromModule(methodName);
+ if (ret != null) {
+ ret = new MethodCallExpression(ret, "call", args);
+ setSourcePosition(ret, mce);
+ return ret;
+ }
+ }
+ } else if (currentMethod!=null && currentMethod.isStatic() && isExplicitSuper) {
+ MethodCallExpression ret = new MethodCallExpression(new ClassExpression(currentClass.getSuperClass()), method, args);
+ setSourcePosition(ret, mce);
+ return ret;
+ }
+
+ if (method instanceof ConstantExpression) {
+ ConstantExpression ce = (ConstantExpression) method;
+ Object value = ce.getValue();
+ if (value instanceof String) {
+ boolean foundInstanceMethod = false;
+ String methodName = (String) value;
+ boolean inInnerClass = isInnerClass(currentClass);
+ if (currentMethod != null && !currentMethod.isStatic()) {
+ if (currentClass.hasPossibleMethod(methodName, args)) {
+ foundInstanceMethod = true;
+ }
+ }
+ boolean lookForPossibleStaticMethod = !methodName.equals("call");
+ lookForPossibleStaticMethod &= !foundInstanceMethod;
+ lookForPossibleStaticMethod |= inSpecialConstructorCall;
+ lookForPossibleStaticMethod &= !inInnerClass;
+ if (!inClosure && lookForPossibleStaticMethod &&
+ (hasPossibleStaticMethod(currentClass, methodName, args, true))
+ || hasPossibleStaticProperty(currentClass, methodName)) {
+ StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args);
+ setSourcePosition(smce, mce);
+ return smce;
+ }
+ if (!inClosure && inInnerClass && inSpecialConstructorCall && mce.isImplicitThis() && !foundInstanceMethod) {
+ if (currentClass.getOuterClass().hasPossibleMethod(methodName, args)) {
+ object = new PropertyExpression(new ClassExpression(currentClass.getOuterClass()), new ConstantExpression("this"));
+ } else if (hasPossibleStaticMethod(currentClass.getOuterClass(), methodName, args, true)
+ || hasPossibleStaticProperty(currentClass.getOuterClass(), methodName)) {
+ StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass.getOuterClass(), methodName, args);
+ setSourcePosition(smce, mce);
+ return smce;
+ }
+ }
+ }
+ }
+ }
+
+ MethodCallExpression result = new MethodCallExpression(object, method, args);
+ result.setSafe(mce.isSafe());
+ result.setImplicitThis(mce.isImplicitThis());
+ result.setSpreadSafe(mce.isSpreadSafe());
+ result.setMethodTarget(mce.getMethodTarget());
+ // GROOVY-6757
+ result.setGenericsTypes(mce.getGenericsTypes());
+ setSourcePosition(result, mce);
+ return result;
+ }
+
+ protected Expression transformConstructorCallExpression(ConstructorCallExpression cce) {
+ inSpecialConstructorCall = cce.isSpecialCall();
+ Expression expression = cce.getArguments();
+ if (expression instanceof TupleExpression) {
+ TupleExpression tuple = (TupleExpression) expression;
+ if (tuple.getExpressions().size() == 1) {
+ expression = tuple.getExpression(0);
+ if (expression instanceof NamedArgumentListExpression) {
+ NamedArgumentListExpression namedArgs = (NamedArgumentListExpression) expression;
+ List<MapEntryExpression> entryExpressions = namedArgs.getMapEntryExpressions();
+ for (int i = 0; i < entryExpressions.size(); i++) {
+ entryExpressions.set(i, (MapEntryExpression) transformMapEntryExpression(entryExpressions.get(i), cce.getType()));
+ }
+ }
+ }
+ }
+ Expression ret = cce.transformExpression(this);
+ inSpecialConstructorCall = false;
+ return ret;
+ }
+
+ protected Expression transformClosureExpression(ClosureExpression ce) {
+ boolean oldInClosure = inClosure;
+ inClosure = true;
+ if (ce.getParameters() != null) {
+ for (Parameter p : ce.getParameters()) {
+ if (p.hasInitialExpression()) {
+ p.setInitialExpression(transform(p.getInitialExpression()));
+ }
+ }
+ }
+ Statement code = ce.getCode();
+ if (code != null) code.visit(this);
+ inClosure = oldInClosure;
+ return ce;
+ }
+
+ protected Expression transformPropertyExpression(PropertyExpression pe) {
+ if (currentMethod!=null && currentMethod.isStatic()
+ && pe.getObjectExpression() instanceof VariableExpression
+ && ((VariableExpression) pe.getObjectExpression()).isSuperExpression()) {
+ PropertyExpression pexp = new PropertyExpression(
+ new ClassExpression(currentClass.getSuperClass()),
+ transform(pe.getProperty())
+ );
+ pexp.setSourcePosition(pe);
+ return pexp;
+ }
+ boolean oldInPropertyExpression = inPropertyExpression;
+ Expression oldFoundArgs = foundArgs;
+ Expression oldFoundConstant = foundConstant;
+ inPropertyExpression = true;
+ foundArgs = null;
+ foundConstant = null;
+ Expression objectExpression = transform(pe.getObjectExpression());
+ boolean candidate = false;
+ if (objectExpression instanceof MethodCallExpression) {
+ candidate = ((MethodCallExpression)objectExpression).isImplicitThis();
+ }
+
+ if (foundArgs != null && foundConstant != null && candidate) {
+ Expression result = findStaticMethodImportFromModule(foundConstant, foundArgs);
+ if (result != null) {
+ objectExpression = result;
+ objectExpression.setSourcePosition(pe);
+ }
+ }
+ inPropertyExpression = oldInPropertyExpression;
+ foundArgs = oldFoundArgs;
+ foundConstant = oldFoundConstant;
+ pe.setObjectExpression(objectExpression);
+ return pe;
+ }
+
+ private Expression findStaticFieldOrPropAccessorImportFromModule(String name) {
+ ModuleNode module = currentClass.getModule();
+ if (module == null) return null;
+ Map<String, ImportNode> importNodes = module.getStaticImports();
+ Expression expression;
+ String accessorName = getAccessorName(name);
+ // look for one of these:
+ // import static MyClass.setProp [as setOtherProp]
+ // import static MyClass.getProp [as getOtherProp]
+ // when resolving prop reference
+ if (importNodes.containsKey(accessorName)) {
+ expression = findStaticProperty(importNodes, accessorName);
+ if (expression != null) return expression;
+ }
+ if (accessorName.startsWith("get")) {
+ accessorName = "is" + accessorName.substring(3);
+ if (importNodes.containsKey(accessorName)) {
+ expression = findStaticProperty(importNodes, accessorName);
+ if (expression != null) return expression;
+ }
+ }
+
+ // look for one of these:
+ // import static MyClass.prop [as otherProp]
+ // when resolving prop or field reference
+ if (importNodes.containsKey(name)) {
+ ImportNode importNode = importNodes.get(name);
+ expression = findStaticPropertyAccessor(importNode.getType(), importNode.getFieldName());
+ if (expression != null) return expression;
+ expression = findStaticField(importNode.getType(), importNode.getFieldName());
+ if (expression != null) return expression;
+ }
+ // look for one of these:
+ // import static MyClass.*
+ // when resolving prop or field reference
+ for (ImportNode importNode : module.getStaticStarImports().values()) {
+ ClassNode node = importNode.getType();
+ expression = findStaticPropertyAccessor(node, name);
+ if (expression != null) return expression;
+ expression = findStaticField(node, name);
+ if (expression != null) return expression;
+ }
+ return null;
+ }
+
+ private Expression findStaticProperty(Map<String, ImportNode> importNodes, String accessorName) {
+ Expression result = null;
+ ImportNode importNode = importNodes.get(accessorName);
+ ClassNode importClass = importNode.getType();
+ String importMember = importNode.getFieldName();
+ result = findStaticPropertyAccessorByFullName(importClass, importMember);
+ if (result == null) {
+ result = findStaticPropertyAccessor(importClass, getPropNameForAccessor(importMember));
+ }
+ return result;
+ }
+
+ private Expression findStaticMethodImportFromModule(Expression method, Expression args) {
+ ModuleNode module = currentClass.getModule();
+ if (module == null || !(method instanceof ConstantExpression)) return null;
+ Map<String, ImportNode> importNodes = module.getStaticImports();
+ ConstantExpression ce = (ConstantExpression) method;
+ Expression expression;
+ Object value = ce.getValue();
+ // skip non-Strings, e.g. Integer
+ if (!(value instanceof String)) return null;
+ final String name = (String) value;
+ // look for one of these:
+ // import static SomeClass.method [as otherName]
+ // when resolving methodCall() or getProp() or setProp()
+ if (importNodes.containsKey(name)) {
+ ImportNode importNode = importNodes.get(name);
+ expression = findStaticMethod(importNode.getType(), importNode.getFieldName(), args);
+ if (expression != null) return expression;
+ expression = findStaticPropertyAccessorGivenArgs(importNode.getType(), getPropNameForAccessor(importNode.getFieldName()), args);
+ if (expression != null) {
+ return new StaticMethodCallExpression(importNode.getType(), importNode.getFieldName(), args);
+ }
+ }
+ // look for one of these:
+ // import static SomeClass.someProp [as otherName]
+ // when resolving getProp() or setProp()
+ if (isValidAccessorName(name)) {
+ String propName = getPropNameForAccessor(name);
+ if (importNodes.containsKey(propName)) {
+ ImportNode importNode = importNodes.get(propName);
+ ClassNode importClass = importNode.getType();
+ String importMember = importNode.getFieldName();
+ expression = findStaticMethod(importClass, prefix(name) + capitalize(importMember), args);
+ if (expression != null) return expression;
+ expression = findStaticPropertyAccessorGivenArgs(importClass, importMember, args);
+ if (expression != null) {
+ return new StaticMethodCallExpression(importClass, prefix(name) + capitalize(importMember), args);
+ }
+ }
+ }
+ Map<String, ImportNode> starImports = module.getStaticStarImports();
+ ClassNode starImportType;
+ if (currentClass.isEnum() && starImports.containsKey(currentClass.getName())) {
+ ImportNode importNode = starImports.get(currentClass.getName());
+ starImportType = importNode == null ? null : importNode.getType();
+ expression = findStaticMethod(starImportType, name, args);
+ if (expression != null) return expression;
+ } else {
+ for (ImportNode importNode : starImports.values()) {
+ starImportType = importNode == null ? null : importNode.getType();
+ expression = findStaticMethod(starImportType, name, args);
+ if (expression != null) return expression;
+ expression = findStaticPropertyAccessorGivenArgs(starImportType, getPropNameForAccessor(name), args);
+ if (expression != null) {
+ return new StaticMethodCallExpression(starImportType, name, args);
+ }
+ }
+ }
+ return null;
+ }
+
+ private static String prefix(String name) {
+ return name.startsWith("is") ? "is" : name.substring(0, 3);
+ }
+
+ private String getAccessorName(String name) {
+ return (inLeftExpression ? "set" : "get") + capitalize(name);
+ }
+
+ private Expression findStaticPropertyAccessorGivenArgs(ClassNode staticImportType, String propName, Expression args) {
+ // TODO validate args?
+ return findStaticPropertyAccessor(staticImportType, propName);
+ }
+
+ private Expression findStaticPropertyAccessor(ClassNode staticImportType, String propName) {
+ String accessorName = getAccessorName(propName);
+ Expression accessor = findStaticPropertyAccessorByFullName(staticImportType, accessorName);
+ if (accessor == null && accessorName.startsWith("get")) {
+ accessor = findStaticPropertyAccessorByFullName(staticImportType, "is" + accessorName.substring(3));
+ }
+ if (accessor == null && hasStaticProperty(staticImportType, propName)) {
+ // args will be replaced
+ if (inLeftExpression)
+ accessor = new StaticMethodCallExpression(staticImportType, accessorName, ArgumentListExpression.EMPTY_ARGUMENTS);
+ else
+ accessor = new PropertyExpression(new ClassExpression(staticImportType), propName);
+ }
+ return accessor;
+ }
+
+ private Expression findStaticPropertyAccessorByFullName(ClassNode staticImportType, String accessorMethodName) {
+ // anything will do as we only check size == 1
+ ArgumentListExpression dummyArgs = new ArgumentListExpression();
+ dummyArgs.addExpression(EmptyExpression.INSTANCE);
+ return findStaticMethod(staticImportType, accessorMethodName, (inLeftExpression ? dummyArgs : ArgumentListExpression.EMPTY_ARGUMENTS));
+ }
+
+ private static Expression findStaticField(ClassNode staticImportType, String fieldName) {
+ if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
+ FieldNode field = getField(staticImportType, fieldName);
+ if (field != null && field.isStatic())
+ return new PropertyExpression(new ClassExpression(staticImportType), fieldName);
+ }
+ return null;
+ }
+
+ private static FieldNode getField(ClassNode classNode, String fieldName) {
+ ClassNode node = classNode;
+ Set<String> visited = new HashSet<String>();
+ while (node != null) {
+ FieldNode fn = node.getDeclaredField(fieldName);
+ if (fn != null) return fn;
+ ClassNode[] interfaces = node.getInterfaces();
+ for (ClassNode iNode : interfaces) {
+ if (visited.contains(iNode.getName())) continue;
+ FieldNode ifn = getField(iNode, fieldName);
+ visited.add(iNode.getName());
+ if (ifn != null) return ifn;
+ }
+ node = node.getSuperClass();
+ }
+ return null;
+ }
+
+ private static Expression findStaticMethod(ClassNode staticImportType, String methodName, Expression args) {
+ if (staticImportType.isPrimaryClassNode() || staticImportType.isResolved()) {
+ if (staticImportType.hasPossibleStaticMethod(methodName, args)) {
+ return new StaticMethodCallExpression(staticImportType, methodName, args);
+ }
+ }
+ return null;
+ }
+
+ protected SourceUnit getSourceUnit() {
+ return source;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/StaticVerifier.java b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
new file mode 100644
index 0000000..53706f9
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/StaticVerifier.java
@@ -0,0 +1,204 @@
+/*
+ * 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.control;
+
+import org.codehaus.groovy.ast.ClassCodeVisitorSupport;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.CodeVisitorSupport;
+import org.codehaus.groovy.ast.DynamicVariable;
+import org.codehaus.groovy.ast.FieldNode;
+import org.codehaus.groovy.ast.MethodNode;
+import org.codehaus.groovy.ast.Parameter;
+import org.codehaus.groovy.ast.Variable;
+import org.codehaus.groovy.ast.expr.ClosureExpression;
+import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
+import org.codehaus.groovy.ast.expr.Expression;
+import org.codehaus.groovy.ast.expr.MethodCallExpression;
+import org.codehaus.groovy.ast.expr.PropertyExpression;
+import org.codehaus.groovy.ast.expr.VariableExpression;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import static org.apache.groovy.ast.tools.ClassNodeUtils.isInnerClass;
+
+/**
+ * Verifier to check non-static access in static contexts
+ */
+public class StaticVerifier extends ClassCodeVisitorSupport {
+ private boolean inSpecialConstructorCall;
+ private boolean inPropertyExpression; // TODO use it or lose it
+ private boolean inClosure;
+ private MethodNode currentMethod;
+ private SourceUnit source;
+
+ public void visitClass(ClassNode node, SourceUnit source) {
+ this.source = source;
+ super.visitClass(node);
+ }
+
+ @Override
+ public void visitVariableExpression(VariableExpression ve) {
+ Variable v = ve.getAccessedVariable();
+ if (v != null && v instanceof DynamicVariable) {
+ if (!inPropertyExpression || inSpecialConstructorCall) addStaticVariableError(ve);
+ }
+ }
+
+ @Override
+ public void visitClosureExpression(ClosureExpression ce) {
+ boolean oldInClosure = inClosure;
+ inClosure = true;
+ super.visitClosureExpression(ce);
+ inClosure = oldInClosure;
+ }
+
+ @Override
+ public void visitConstructorCallExpression(ConstructorCallExpression cce) {
+ boolean oldIsSpecialConstructorCall = inSpecialConstructorCall;
+ inSpecialConstructorCall = cce.isSpecialCall();
+ super.visitConstructorCallExpression(cce);
+ inSpecialConstructorCall = oldIsSpecialConstructorCall;
+ }
+
+ @Override
+ public void visitConstructorOrMethod(MethodNode node, boolean isConstructor) {
+ MethodNode oldCurrentMethod = currentMethod;
+ currentMethod = node;
+ super.visitConstructorOrMethod(node, isConstructor);
+ if (isConstructor) {
+ final Set<String> exceptions = new HashSet<String>();
+ for (final Parameter param : node.getParameters()) {
+ exceptions.add(param.getName());
+ if (param.hasInitialExpression()) {
+ param.getInitialExpression().visit(new CodeVisitorSupport() {
+ @Override
+ public void visitVariableExpression(VariableExpression ve) {
+ if (exceptions.contains(ve.getName())) return;
+ Variable av = ve.getAccessedVariable();
+ if (av instanceof DynamicVariable || !av.isInStaticContext()) {
+ addVariableError(ve);
+ }
+ }
+
+ @Override
+ public void visitMethodCallExpression(MethodCallExpression call) {
+ Expression objectExpression = call.getObjectExpression();
+ if (objectExpression instanceof VariableExpression) {
+ VariableExpression ve = (VariableExpression) objectExpression;
+ if (ve.isThisExpression()) {
+ addError("Can't access instance method '" + call.getMethodAsString() + "' for a constructor parameter default value", param);
+ return;
+ }
+ }
+ super.visitMethodCallExpression(call);
+ }
+
+ @Override
+ public void visitClosureExpression(ClosureExpression expression) {
+ //skip contents, because of dynamic scope
+ }
+ });
+ }
+ }
+ }
+ currentMethod = oldCurrentMethod;
+ }
+
+ @Override
+ public void visitMethodCallExpression(MethodCallExpression mce) {
+ if (inSpecialConstructorCall && !isInnerClass(currentMethod.getDeclaringClass())) {
+ Expression objectExpression = mce.getObjectExpression();
+ if (objectExpression instanceof VariableExpression) {
+ VariableExpression ve = (VariableExpression) objectExpression;
+ if (ve.isThisExpression()) {
+ addError("Can't access instance method '" + mce.getMethodAsString() + "' before the class is constructed", mce);
+ return;
+ }
+ }
+ }
+ super.visitMethodCallExpression(mce);
+ }
+
+ @Override
+ public void visitPropertyExpression(PropertyExpression pe) {
+ if (!inSpecialConstructorCall) checkStaticScope(pe);
+ }
+
+ @Override
+ protected SourceUnit getSourceUnit() {
+ return source;
+ }
+
+
+ private void checkStaticScope(PropertyExpression pe) {
+ if (inClosure) return;
+ for (Expression it = pe; it != null; it = ((PropertyExpression) it).getObjectExpression()) {
+ if (it instanceof PropertyExpression) continue;
+ if (it instanceof VariableExpression) {
+ addStaticVariableError((VariableExpression) it);
+ }
+ return;
+ }
+ }
+
+ private void addStaticVariableError(VariableExpression ve) {
+ // closures are always dynamic
+ // propertyExpressions will handle the error a bit differently
+ if (!inSpecialConstructorCall && (inClosure || !ve.isInStaticContext())) return;
+ if (ve.isThisExpression() || ve.isSuperExpression()) return;
+ Variable v = ve.getAccessedVariable();
+ if (currentMethod != null && currentMethod.isStatic()) {
+ FieldNode fieldNode = getDeclaredOrInheritedField(currentMethod.getDeclaringClass(), ve.getName());
+ if (fieldNode != null && fieldNode.isStatic()) return;
+ }
+ if (v != null && !(v instanceof DynamicVariable) && v.isInStaticContext()) return;
+ addVariableError(ve);
+ }
+
+ private void addVariableError(VariableExpression ve) {
+ addError("Apparent variable '" + ve.getName() + "' was found in a static scope but doesn't refer" +
+ " to a local variable, static field or class. Possible causes:\n" +
+ "You attempted to reference a variable in the binding or an instance variable from a static context.\n" +
+ "You misspelled a classname or statically imported field. Please check the spelling.\n" +
+ "You attempted to use a method '" + ve.getName() +
+ "' but left out brackets in a place not allowed by the grammar.", ve);
+ }
+
+ private static FieldNode getDeclaredOrInheritedField(ClassNode cn, String fieldName) {
+ ClassNode node = cn;
+ while (node != null) {
+ FieldNode fn = node.getDeclaredField(fieldName);
+ if (fn != null) return fn;
+ List<ClassNode> interfacesToCheck = new ArrayList<ClassNode>(Arrays.asList(node.getInterfaces()));
+ while (!interfacesToCheck.isEmpty()) {
+ ClassNode nextInterface = interfacesToCheck.remove(0);
+ fn = nextInterface.getDeclaredField(fieldName);
+ if (fn != null) return fn;
+ interfacesToCheck.addAll(Arrays.asList(nextInterface.getInterfaces()));
+ }
+ node = node.getSuperClass();
+ }
+ return null;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/XStreamUtils.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/XStreamUtils.java b/src/main/java/org/codehaus/groovy/control/XStreamUtils.java
new file mode 100644
index 0000000..3f7ccd3
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/XStreamUtils.java
@@ -0,0 +1,68 @@
+/*
+ * 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.control;
+
+import com.thoughtworks.xstream.XStream;
+import com.thoughtworks.xstream.io.xml.StaxDriver;
+import org.codehaus.groovy.runtime.DefaultGroovyMethods;
+
+import java.io.File;
+import java.io.FileWriter;
+import java.net.URI;
+
+public abstract class XStreamUtils {
+
+ public static void serialize(final String name, final Object ast) {
+ if (name == null || name.length() == 0) return;
+
+ XStream xstream = new XStream(new StaxDriver());
+ FileWriter astFileWriter = null;
+ try {
+ File astFile = astFile(name);
+ if (astFile == null) {
+ System.out.println("File-name for writing " + name + " AST could not be determined!");
+ return;
+ }
+ astFileWriter = new FileWriter(astFile, false);
+ xstream.toXML(ast, astFileWriter);
+ System.out.println("Written AST to " + name + ".xml");
+
+ } catch (Exception e) {
+ System.out.println("Couldn't write to " + name + ".xml");
+ e.printStackTrace();
+ } finally {
+ DefaultGroovyMethods.closeQuietly(astFileWriter);
+ }
+ }
+
+ /**
+ * Takes the incoming file-name and checks whether this is a URI using the <tt>file:</tt> protocol or a non-URI and treats
+ * it accordingly.
+ *
+ * @return a file-name {@link java.io.File} representation or <tt>null</tt> if the file-name was in an invalid URI format
+ */
+ private static File astFile(final String uriOrFileName) {
+ try {
+ final String astFileName = uriOrFileName + ".xml";
+ return uriOrFileName.startsWith("file:") ? new File(URI.create(astFileName)) : new File(astFileName);
+ } catch (IllegalArgumentException e) {
+ return null;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/customizers/CompilationCustomizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/customizers/CompilationCustomizer.java b/src/main/java/org/codehaus/groovy/control/customizers/CompilationCustomizer.java
new file mode 100644
index 0000000..8f9e81c
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/customizers/CompilationCustomizer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.control.customizers;
+
+import org.codehaus.groovy.control.CompilationUnit;
+import org.codehaus.groovy.control.CompilePhase;
+
+/**
+ * Users wanting to customize the configuration process such as adding imports, restricting the
+ * language features or apply AST transformations by default should implement this class, then
+ * call the {@link org.codehaus.groovy.control.CompilerConfiguration#addCompilationCustomizers(CompilationCustomizer...)}
+ * method.
+ *
+ * @author Cedric Champeau
+ *
+ * @since 1.8.0
+ *
+ */
+public abstract class CompilationCustomizer extends CompilationUnit.PrimaryClassNodeOperation {
+ private final CompilePhase phase;
+
+ public CompilationCustomizer(CompilePhase phase) {
+ this.phase = phase;
+ }
+
+ public CompilePhase getPhase() {
+ return phase;
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/customizers/DelegatingCustomizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/customizers/DelegatingCustomizer.java b/src/main/java/org/codehaus/groovy/control/customizers/DelegatingCustomizer.java
new file mode 100644
index 0000000..0f6af6a
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/customizers/DelegatingCustomizer.java
@@ -0,0 +1,45 @@
+/*
+ * 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.control.customizers;
+
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.SourceUnit;
+
+/**
+ * Base class for compilation customizers which delegate to another customizer. The phase this
+ * customizer runs at is retrieved from the phase of the delegate.
+ *
+ * @author Cedric Champeau
+ * @since 2.1.0
+ */
+public abstract class DelegatingCustomizer extends CompilationCustomizer {
+ protected final CompilationCustomizer delegate;
+
+ public DelegatingCustomizer(CompilationCustomizer delegate) {
+ super(delegate.getPhase());
+ this.delegate = delegate;
+ }
+
+ @Override
+ public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException {
+ delegate.call(source, context, classNode);
+ }
+}
http://git-wip-us.apache.org/repos/asf/groovy/blob/b25d0e55/src/main/java/org/codehaus/groovy/control/customizers/ImportCustomizer.java
----------------------------------------------------------------------
diff --git a/src/main/java/org/codehaus/groovy/control/customizers/ImportCustomizer.java b/src/main/java/org/codehaus/groovy/control/customizers/ImportCustomizer.java
new file mode 100644
index 0000000..780b554
--- /dev/null
+++ b/src/main/java/org/codehaus/groovy/control/customizers/ImportCustomizer.java
@@ -0,0 +1,169 @@
+/*
+ * 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.control.customizers;
+
+import org.codehaus.groovy.ast.ClassHelper;
+import org.codehaus.groovy.ast.ClassNode;
+import org.codehaus.groovy.ast.ModuleNode;
+import org.codehaus.groovy.classgen.GeneratorContext;
+import org.codehaus.groovy.control.CompilationFailedException;
+import org.codehaus.groovy.control.CompilePhase;
+import org.codehaus.groovy.control.SourceUnit;
+
+import java.util.LinkedList;
+import java.util.List;
+
+/**
+ * This compilation customizer allows addiing various types of imports to the compilation unit. Supports adding :
+ * <ul>
+ * <li>standard imports thanks to {@link #addImport(String)}, {@link #addImport(String, String)} or {@link #addImports(String...)}</li>
+ * <li>star imports thanks to {@link #addStarImport(String)} or {@link #addStarImports(String...)}</li>
+ * <li>static imports thanks to {@link #addStaticImport(String, String)} or {@link #addStaticImport(String, String, String)}</li>
+ * <li>static star imports thanks to {@link #addStaticStar(String)} or {@link #addStaticStars(String...)}</li>
+ * </ul>
+ *
+ * @author Cedric Champeau
+ *
+ * @since 1.8.0
+ *
+ */
+public class ImportCustomizer extends CompilationCustomizer {
+
+ private final List<Import> imports = new LinkedList<Import>();
+
+ public ImportCustomizer() {
+ super(CompilePhase.CONVERSION);
+ }
+
+ @Override
+ public void call(final SourceUnit source, final GeneratorContext context, final ClassNode classNode) throws CompilationFailedException {
+ final ModuleNode ast = source.getAST();
+ for (Import anImport : imports) {
+ switch (anImport.type) {
+ case regular:
+ ast.addImport(anImport.alias, anImport.classNode);
+ break;
+ case staticImport:
+ ast.addStaticImport(anImport.classNode, anImport.field, anImport.alias);
+ break;
+ case staticStar:
+ ast.addStaticStarImport(anImport.alias, anImport.classNode);
+ break;
+ case star:
+ ast.addStarImport(anImport.star);
+ break;
+ }
+ }
+ }
+
+ public ImportCustomizer addImport(final String alias, final String className) {
+ imports.add(new Import(ImportType.regular, alias, ClassHelper.make(className)));
+ return this;
+ }
+
+ public ImportCustomizer addStaticImport(final String className, final String fieldName) {
+ final ClassNode node = ClassHelper.make(className);
+ imports.add(new Import(ImportType.staticImport, fieldName, node, fieldName));
+ return this;
+ }
+
+ public ImportCustomizer addStaticStars(final String... classNames) {
+ for (String className : classNames) {
+ addStaticStar(className);
+ }
+ return this;
+ }
+
+ public ImportCustomizer addStaticImport(final String alias, final String className, final String fieldName) {
+ imports.add(new Import(ImportCustomizer.ImportType.staticImport, alias, ClassHelper.make(className), fieldName));
+ return this;
+ }
+
+ public ImportCustomizer addImports(final String... imports) {
+ for (String anImport : imports) {
+ addImport(anImport);
+ }
+ return this;
+ }
+
+ public ImportCustomizer addStarImports(final String... packageNames) {
+ for (String packageName : packageNames) {
+ addStarImport(packageName);
+ }
+ return this;
+ }
+
+ private void addImport(final String className) {
+ final ClassNode node = ClassHelper.make(className);
+ imports.add(new Import(ImportType.regular, node.getNameWithoutPackage(), node));
+ }
+
+ private void addStaticStar(final String className) {
+ imports.add(new Import(ImportCustomizer.ImportType.staticStar, className, ClassHelper.make(className)));
+ }
+
+ private void addStarImport(final String packagename) {
+ final String packageNameEndingWithDot = packagename.endsWith(".")?packagename:packagename+'.';
+ imports.add(new Import(ImportType.star,packageNameEndingWithDot));
+ }
+
+ // -------------------- Helper classes -------------------------
+
+ /**
+ * Represents imports which are possibly aliased.
+ */
+ private static final class Import {
+ final ImportType type;
+ final ClassNode classNode;
+ final String alias;
+ final String field;
+ final String star; // only used for star imports
+
+ private Import(final ImportType type, final String alias, final ClassNode classNode, final String field) {
+ this.alias = alias;
+ this.classNode = classNode;
+ this.field = field;
+ this.type = type;
+ this.star = null;
+ }
+
+ private Import(final ImportType type, final String alias, final ClassNode classNode) {
+ this.alias = alias;
+ this.classNode = classNode;
+ this.type = type;
+ this.field = null;
+ this.star = null;
+ }
+
+ private Import(final ImportType type, final String star) {
+ this.type = type;
+ this.star = star;
+ this.alias = null;
+ this.classNode = null;
+ this.field = null;
+ }
+ }
+
+ private enum ImportType {
+ regular,
+ staticImport,
+ staticStar,
+ star
+ }
+}