http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java new file mode 100644 index 0000000..2222590 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassSignatureParser.java @@ -0,0 +1,84 @@ +/* + * 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.ClassNode; +import org.codehaus.groovy.ast.GenericsType; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** + * @author Peter Gromov + */ +class ClassSignatureParser { + static void configureClass(ClassNode classNode, ClassStub stub, AsmReferenceResolver resolver) { + if (stub.signature != null) { + parseClassSignature(classNode, stub.signature, resolver); + return; + } + + if (stub.superName != null) { + classNode.setSuperClass(resolver.resolveClass(AsmDecompiler.fromInternalName(stub.superName))); + } + + ClassNode[] interfaces = new ClassNode[stub.interfaceNames.length]; + for (int i = 0; i < stub.interfaceNames.length; i++) { + interfaces[i] = resolver.resolveClass(AsmDecompiler.fromInternalName(stub.interfaceNames[i])); + } + classNode.setInterfaces(interfaces); + } + + private static void parseClassSignature(final ClassNode classNode, String signature, final AsmReferenceResolver resolver) { + final List<ClassNode> interfaces = new ArrayList<ClassNode>(); + FormalParameterParser v = new FormalParameterParser(resolver) { + + @Override + public SignatureVisitor visitSuperclass() { + flushTypeParameter(); + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + classNode.setSuperClass(result); + } + }; + } + + @Override + public SignatureVisitor visitInterface() { + flushTypeParameter(); + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + interfaces.add(result); + } + }; + } + + }; + new SignatureReader(signature).accept(v); + GenericsType[] typeParameters = v.getTypeParameters(); + if (typeParameters.length > 0) { + classNode.setGenericsTypes(typeParameters); + } + classNode.setInterfaces(interfaces.toArray(new ClassNode[interfaces.size()])); + } +}
http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java new file mode 100644 index 0000000..364b69d --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/ClassStub.java @@ -0,0 +1,117 @@ +/* + * 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 java.util.ArrayList; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +/** + * @author Peter Gromov + */ +public class ClassStub extends MemberStub { + final String className; + final int accessModifiers; + final String signature; + final String superName; + final String[] interfaceNames; + List<MethodStub> methods; + List<FieldStub> fields; + final Map<String, Integer> innerClassModifiers = new HashMap<String, Integer>(); + + public ClassStub(String className, int accessModifiers, String signature, String superName, String[] interfaceNames) { + this.className = className; + this.accessModifiers = accessModifiers; + this.signature = signature; + this.superName = superName; + this.interfaceNames = interfaceNames; + } +} + +class MemberStub { + List<AnnotationStub> annotations = null; + + AnnotationStub addAnnotation(String desc) { + AnnotationStub stub = new AnnotationStub(desc); + if (annotations == null) annotations = new ArrayList<AnnotationStub>(1); + annotations.add(stub); + return stub; + } +} + +class MethodStub extends MemberStub { + final String methodName; + final int accessModifiers; + final String desc; + final String signature; + final String[] exceptions; + Map<Integer, List<AnnotationStub>> parameterAnnotations; + Object annotationDefault; + + public MethodStub(String methodName, int accessModifiers, String desc, String signature, String[] exceptions) { + this.methodName = methodName; + this.accessModifiers = accessModifiers; + this.desc = desc; + this.signature = signature; + this.exceptions = exceptions; + } +} + +class FieldStub extends MemberStub { + final String fieldName; + final int accessModifiers; + final String desc; + final String signature; + + public FieldStub(String fieldName, int accessModifiers, String desc, String signature) { + this.fieldName = fieldName; + this.accessModifiers = accessModifiers; + this.desc = desc; + this.signature = signature; + } +} + +class AnnotationStub { + final String className; + final Map<String, Object> members = new LinkedHashMap<String, Object>(); + + public AnnotationStub(String className) { + this.className = className; + } +} + +class TypeWrapper { + final String desc; + + public TypeWrapper(String desc) { + this.desc = desc; + } +} + +class EnumConstantWrapper { + final String enumDesc; + final String constant; + + public EnumConstantWrapper(String enumDesc, String constant) { + this.enumDesc = enumDesc; + this.constant = constant; + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java new file mode 100644 index 0000000..18cc79f --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/DecompiledClassNode.java @@ -0,0 +1,242 @@ +/* + * 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.AnnotatedNode; +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.MixinNode; +import org.codehaus.groovy.classgen.Verifier; + +import java.lang.reflect.Modifier; +import java.util.List; + +/** + * A {@link ClassNode} kind representing the classes coming from *.class files decompiled using ASM. + * + * @see AsmDecompiler + * @author Peter Gromov + */ +public class DecompiledClassNode extends ClassNode { + private final ClassStub classData; + private final AsmReferenceResolver resolver; + private boolean supersInitialized = false; + private boolean membersInitialized = false; + + public DecompiledClassNode(ClassStub data, AsmReferenceResolver resolver) { + super(data.className, getFullModifiers(data, resolver), null, null, MixinNode.EMPTY_ARRAY); + classData = data; + this.resolver = resolver; + isPrimaryNode = false; + } + + /** + * Static inner classes don't have "static" part in their own modifiers. Their containing classes have to be inspected + * whether they have an inner class with the same name that's static. '$' separator convention is used to search + * for parent classes. + */ + private static int getFullModifiers(ClassStub data, AsmReferenceResolver resolver) { + String className = data.className; + int bound = className.length(); + while (bound > 0) { + int idx = className.lastIndexOf('$', bound); + if (idx > 0) { + ClassNode outerClass = resolver.resolveClassNullable(className.substring(0, idx)); + if (outerClass instanceof DecompiledClassNode) { + Integer outerModifiers = ((DecompiledClassNode) outerClass).classData.innerClassModifiers.get(className.substring(idx + 1)); + if (outerModifiers != null) { + return data.accessModifiers | outerModifiers; + } + } + } + bound = idx - 1; + } + return data.accessModifiers; + } + + public long getCompilationTimeStamp() { + if (classData.fields != null) { + for (FieldStub field : classData.fields) { + if (Modifier.isStatic(field.accessModifiers)) { + Long timestamp = Verifier.getTimestampFromFieldName(field.fieldName); + if (timestamp != null) { + return timestamp; + } + } + } + } + return Long.MAX_VALUE; + } + + @Override + public GenericsType[] getGenericsTypes() { + lazyInitSupers(); + return super.getGenericsTypes(); + } + + @Override + public boolean isUsingGenerics() { + lazyInitSupers(); + return super.isUsingGenerics(); + } + + @Override + public List<FieldNode> getFields() { + lazyInitMembers(); + return super.getFields(); + } + + @Override + public ClassNode[] getInterfaces() { + lazyInitSupers(); + return super.getInterfaces(); + } + + @Override + public List<MethodNode> getMethods() { + lazyInitMembers(); + return super.getMethods(); + } + + @Override + public List<ConstructorNode> getDeclaredConstructors() { + lazyInitMembers(); + return super.getDeclaredConstructors(); + } + + @Override + public FieldNode getDeclaredField(String name) { + lazyInitMembers(); + return super.getDeclaredField(name); + } + + @Override + public List<MethodNode> getDeclaredMethods(String name) { + lazyInitMembers(); + return super.getDeclaredMethods(name); + } + + @Override + public ClassNode getUnresolvedSuperClass(boolean useRedirect) { + lazyInitSupers(); + return super.getUnresolvedSuperClass(useRedirect); + } + + @Override + public ClassNode[] getUnresolvedInterfaces(boolean useRedirect) { + lazyInitSupers(); + return super.getUnresolvedInterfaces(useRedirect); + } + + @Override + public List<AnnotationNode> getAnnotations() { + lazyInitSupers(); + return super.getAnnotations(); + } + + @Override + public List<AnnotationNode> getAnnotations(ClassNode type) { + lazyInitSupers(); + return super.getAnnotations(type); + } + + @Override + public void setRedirect(ClassNode cn) { + throw new UnsupportedOperationException(); + } + + @Override + public void setGenericsPlaceHolder(boolean b) { + throw new UnsupportedOperationException(); + } + + @Override + public void setUsingGenerics(boolean b) { + throw new UnsupportedOperationException(); + } + + @Override + public String setName(String name) { + throw new UnsupportedOperationException(); + } + + @Override + public boolean isResolved() { + return true; + } + + @Override + public Class getTypeClass() { + return resolver.resolveJvmClass(getName()); + } + + private void lazyInitSupers() { + synchronized (lazyInitLock) { + if (!supersInitialized) { + ClassSignatureParser.configureClass(this, this.classData, this.resolver); + addAnnotations(classData, this); + supersInitialized = true; + } + } + + } + + private void lazyInitMembers() { + synchronized (lazyInitLock) { + if (!membersInitialized) { + if (classData.methods != null) { + for (MethodStub method : classData.methods) { + MethodNode node = addAnnotations(method, MemberSignatureParser.createMethodNode(resolver, method)); + if (node instanceof ConstructorNode) { + addConstructor((ConstructorNode) node); + } else { + addMethod(node); + } + } + } + + if (classData.fields != null) { + for (FieldStub field : classData.fields) { + addField(addAnnotations(field, MemberSignatureParser.createFieldNode(field, resolver, this))); + } + } + + membersInitialized = true; + } + } + } + + private <T extends AnnotatedNode> T addAnnotations(MemberStub stub, T node) { + List<AnnotationStub> annotations = stub.annotations; + if (annotations != null) { + for (AnnotationStub annotation : annotations) { + AnnotationNode annotationNode = Annotations.createAnnotationNode(annotation, resolver); + if (annotationNode != null) { + node.addAnnotation(annotationNode); + } + } + } + return node; + } + +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/FormalParameterParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/FormalParameterParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/FormalParameterParser.java new file mode 100644 index 0000000..98277d8 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/FormalParameterParser.java @@ -0,0 +1,80 @@ +/* + * 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.ClassNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.vmplugin.v5.Java5; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** +* @author Peter Gromov +*/ +abstract class FormalParameterParser extends SignatureVisitor { + private final AsmReferenceResolver resolver; + private String currentTypeParameter; + private final List<ClassNode> parameterBounds = new ArrayList<ClassNode>(); + private final List<GenericsType> typeParameters = new ArrayList<GenericsType>(); + + public FormalParameterParser(AsmReferenceResolver resolver) { + super(Opcodes.ASM5); + this.resolver = resolver; + } + + @Override + public void visitFormalTypeParameter(String name) { + flushTypeParameter(); + currentTypeParameter = name; + } + + protected void flushTypeParameter() { + if (currentTypeParameter != null) { + ClassNode ref = Java5.configureTypeVariableReference(currentTypeParameter); + ClassNode[] boundNodes = parameterBounds.toArray(new ClassNode[parameterBounds.size()]); + typeParameters.add(Java5.configureTypeVariableDefinition(ref, boundNodes)); + + currentTypeParameter = null; + parameterBounds.clear(); + } + } + + @Override + public SignatureVisitor visitClassBound() { + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + parameterBounds.add(result); + } + }; + } + + @Override + public SignatureVisitor visitInterfaceBound() { + return visitClassBound(); + } + + public GenericsType[] getTypeParameters() { + flushTypeParameter(); + return typeParameters.toArray(new GenericsType[typeParameters.size()]); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/MemberSignatureParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/MemberSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/MemberSignatureParser.java new file mode 100644 index 0000000..6627c85 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/MemberSignatureParser.java @@ -0,0 +1,155 @@ +/* + * 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.ConstructorNode; +import org.codehaus.groovy.ast.FieldNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.ast.MethodNode; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.expr.ConstantExpression; +import org.codehaus.groovy.ast.stmt.ReturnStatement; +import org.objectweb.asm.Type; +import org.objectweb.asm.signature.SignatureReader; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.util.List; +import java.util.Map; + +/** + * @author Peter Gromov + */ +class MemberSignatureParser { + static MethodNode createMethodNode(final AsmReferenceResolver resolver, MethodStub method) { + GenericsType[] typeParameters = null; + + Type[] argumentTypes = Type.getArgumentTypes(method.desc); + final ClassNode[] parameterTypes = new ClassNode[argumentTypes.length]; + for (int i = 0; i < argumentTypes.length; i++) { + parameterTypes[i] = resolver.resolveType(argumentTypes[i]); + } + + final ClassNode[] exceptions = new ClassNode[method.exceptions.length]; + for (int i = 0; i < method.exceptions.length; i++) { + exceptions[i] = resolver.resolveClass(AsmDecompiler.fromInternalName(method.exceptions[i])); + } + + final ClassNode[] returnType = {resolver.resolveType(Type.getReturnType(method.desc))}; + + if (method.signature != null) { + FormalParameterParser v = new FormalParameterParser(resolver) { + int paramIndex = 0; + + @Override + public SignatureVisitor visitParameterType() { + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + parameterTypes[paramIndex] = applyErasure(result, parameterTypes[paramIndex]); + paramIndex++; + } + }; + } + + @Override + public SignatureVisitor visitReturnType() { + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + returnType[0] = applyErasure(result, returnType[0]); + } + }; + } + + int exceptionIndex = 0; + + @Override + public SignatureVisitor visitExceptionType() { + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + exceptions[exceptionIndex] = applyErasure(result, exceptions[exceptionIndex]); + exceptionIndex++; + } + }; + } + }; + new SignatureReader(method.signature).accept(v); + typeParameters = v.getTypeParameters(); + } + + Parameter[] parameters = new Parameter[parameterTypes.length]; + for (int i = 0; i < parameterTypes.length; i++) { + parameters[i] = new Parameter(parameterTypes[i], "param" + i); + } + + if (method.parameterAnnotations != null) { + for (Map.Entry<Integer, List<AnnotationStub>> entry : method.parameterAnnotations.entrySet()) { + for (AnnotationStub stub : entry.getValue()) { + AnnotationNode annotationNode = Annotations.createAnnotationNode(stub, resolver); + if (annotationNode != null) { + parameters[entry.getKey()].addAnnotation(annotationNode); + } + } + } + } + + MethodNode result; + if ("<init>".equals(method.methodName)) { + result = new ConstructorNode(method.accessModifiers, parameters, exceptions, null); + } else { + result = new MethodNode(method.methodName, method.accessModifiers, returnType[0], parameters, exceptions, null); + if (method.annotationDefault != null) { + result.setCode(new ReturnStatement(new ConstantExpression(method.annotationDefault))); + result.setAnnotationDefault(true); + } else { + // Seems wrong but otherwise some tests fail (e.g. TestingASTTransformsTest) + result.setCode(new ReturnStatement(ConstantExpression.NULL)); + } + + } + if (typeParameters != null && typeParameters.length > 0) { + result.setGenericsTypes(typeParameters); + } + return result; + } + + private static ClassNode applyErasure(ClassNode genericType, ClassNode erasure) { + if (genericType.isGenericsPlaceHolder()) { + genericType.setRedirect(erasure); + } + return genericType; + } + + static FieldNode createFieldNode(FieldStub field, AsmReferenceResolver resolver, DecompiledClassNode owner) { + final ClassNode[] type = {resolver.resolveType(Type.getType(field.desc))}; + if (field.signature != null) { + new SignatureReader(field.signature).accept(new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + type[0] = applyErasure(result, type[0]); + } + }); + } + return new FieldNode(field.fieldName, field.accessModifiers, type[0], owner, null); + } +} + http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java new file mode 100644 index 0000000..7d971ed --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/decompiled/TypeSignatureParser.java @@ -0,0 +1,123 @@ +/* + * 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.ClassHelper; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GenericsType; +import org.codehaus.groovy.vmplugin.v5.Java5; +import org.objectweb.asm.Opcodes; +import org.objectweb.asm.Type; +import org.objectweb.asm.signature.SignatureVisitor; + +import java.util.ArrayList; +import java.util.List; + +/** +* @author Peter Gromov +*/ +abstract class TypeSignatureParser extends SignatureVisitor { + private final AsmReferenceResolver resolver; + + public TypeSignatureParser(AsmReferenceResolver resolver) { + super(Opcodes.ASM5); + this.resolver = resolver; + } + + abstract void finished(ClassNode result); + + private String baseName; + private final List<GenericsType> arguments = new ArrayList<GenericsType>(); + + @Override + public void visitTypeVariable(String name) { + finished(Java5.configureTypeVariableReference(name)); + } + + @Override + public void visitBaseType(char descriptor) { + finished(resolver.resolveType(Type.getType(String.valueOf(descriptor)))); + } + + @Override + public SignatureVisitor visitArrayType() { + final TypeSignatureParser outer = this; + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + outer.finished(result.makeArray()); + } + }; + } + + @Override + public void visitClassType(String name) { + baseName = AsmDecompiler.fromInternalName(name); + } + + @Override + public void visitTypeArgument() { + arguments.add(createWildcard(new ClassNode[]{ClassHelper.OBJECT_TYPE}, null)); + } + + @Override + public SignatureVisitor visitTypeArgument(final char wildcard) { + return new TypeSignatureParser(resolver) { + @Override + void finished(ClassNode result) { + if (wildcard == INSTANCEOF) { + arguments.add(new GenericsType(result)); + return; + } + + ClassNode[] upper = wildcard == EXTENDS ? new ClassNode[]{result} : null; + ClassNode lower = wildcard == SUPER ? result : null; + arguments.add(createWildcard(upper, lower)); + } + }; + } + + private static GenericsType createWildcard(ClassNode[] upper, ClassNode lower) { + ClassNode base = ClassHelper.makeWithoutCaching("?"); + base.setRedirect(ClassHelper.OBJECT_TYPE); + GenericsType t = new GenericsType(base, upper, lower); + t.setWildcard(true); + return t; + } + + @Override + public void visitInnerClassType(String name) { + baseName += "$" + name; + arguments.clear(); + } + + @Override + public void visitEnd() { + ClassNode base = resolver.resolveClass(baseName); + if (arguments.isEmpty()) { + finished(base); + return; + } + + ClassNode bound = base.getPlainNodeReference(); + bound.setGenericsTypes(arguments.toArray(new GenericsType[arguments.size()])); + finished(bound); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java new file mode 100644 index 0000000..bb4d78f --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/AnnotationConstantExpression.java @@ -0,0 +1,50 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.AnnotationNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +import java.util.Map; + +/** + * Represents an annotation "constant" that may appear in annotation attributes + * (mainly used as a marker). + * + * @author <a href='mailto:the[dot]mindstorm[at]gmail[dot]com'>Alex Popescu</a> + */ +public class AnnotationConstantExpression extends ConstantExpression { + public AnnotationConstantExpression(AnnotationNode node) { + super(node); + setType(node.getClassNode()); + } + + public void visit(GroovyCodeVisitor visitor) { + AnnotationNode node = (AnnotationNode) getValue(); + Map<String, Expression> attrs = node.getMembers(); + for (Expression expr : attrs.values()) { + expr.visit(visitor); + } + super.visit(visitor); + } + + public String toString() { + return "AnnotationConstantExpression[" + getValue() + "]"; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ArgumentListExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ArgumentListExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ArgumentListExpression.java new file mode 100644 index 0000000..de835b7 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ArgumentListExpression.java @@ -0,0 +1,78 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.Parameter; + +import java.util.List; + +/** + * Represents one or more arguments being passed into a method + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class ArgumentListExpression extends TupleExpression { + + public static final Object[] EMPTY_ARRAY = { + }; + + public static final ArgumentListExpression EMPTY_ARGUMENTS = new ArgumentListExpression(); + + public ArgumentListExpression() { + } + + public ArgumentListExpression(List<Expression> expressions) { + super(expressions); + } + + public ArgumentListExpression(Expression[] expressions) { + super(expressions); + } + + public ArgumentListExpression(Parameter[] parameters) { + for (int i = 0; i < parameters.length; i++) { + Parameter parameter = parameters[i]; + addExpression(new VariableExpression(parameter)); + } + } + + public ArgumentListExpression(Expression expr) { + super(expr); + } + + public ArgumentListExpression(Expression expr1, Expression expr2) { + super(expr1, expr2); + } + + public ArgumentListExpression(Expression expr1, Expression expr2, Expression expr3) { + super(expr1, expr2, expr3); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new ArgumentListExpression(transformExpressions(getExpressions(), transformer)); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitArgumentlistExpression(this); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ArrayExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ArrayExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ArrayExpression.java new file mode 100644 index 0000000..a70102d --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ArrayExpression.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.expr; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +import java.util.Collections; +import java.util.List; + +/** + * Represents an array object construction either using a fixed size + * or an initializer expression + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class ArrayExpression extends Expression { + private final List<Expression> expressions; + private final List<Expression> sizeExpression; + + private final ClassNode elementType; + + private static ClassNode makeArray(ClassNode base, List<Expression> sizeExpression) { + ClassNode ret = base.makeArray(); + if (sizeExpression == null) return ret; + int size = sizeExpression.size(); + for (int i = 1; i < size; i++) { + ret = ret.makeArray(); + } + return ret; + } + + public ArrayExpression(ClassNode elementType, List<Expression> expressions, List<Expression> sizeExpression) { + //expect to get the elementType + super.setType(makeArray(elementType, sizeExpression)); + if (expressions == null) expressions = Collections.emptyList(); + this.elementType = elementType; + this.expressions = expressions; + this.sizeExpression = sizeExpression; + + for (Object item : expressions) { + if (item != null && !(item instanceof Expression)) { + throw new ClassCastException("Item: " + item + " is not an Expression"); + } + } + if (sizeExpression != null) { + for (Object item : sizeExpression) { + if (!(item instanceof Expression)) { + throw new ClassCastException("Item: " + item + " is not an Expression"); + } + } + } + } + + + /** + * Creates an array using an initializer expression + */ + public ArrayExpression(ClassNode elementType, List<Expression> expressions) { + this(elementType, expressions, null); + } + + public void addExpression(Expression expression) { + expressions.add(expression); + } + + public List<Expression> getExpressions() { + return expressions; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitArrayExpression(this); + } + + public boolean isDynamic() { + return false; + } + + public Expression transformExpression(ExpressionTransformer transformer) { + List<Expression> exprList = transformExpressions(expressions, transformer); + List<Expression> sizes = null; + if (sizeExpression != null) sizes = transformExpressions(sizeExpression, transformer); + Expression ret = new ArrayExpression(elementType, exprList, sizes); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public Expression getExpression(int i) { + return expressions.get(i); + } + + public ClassNode getElementType() { + return elementType; + } + + public String getText() { + StringBuilder buffer = new StringBuilder("["); + boolean first = true; + for (Expression expression : expressions) { + if (first) { + first = false; + } else { + buffer.append(", "); + } + + buffer.append(expression.getText()); + } + buffer.append("]"); + return buffer.toString(); + } + + public List<Expression> getSizeExpression() { + return sizeExpression; + } + + public String toString() { + return super.toString() + expressions; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/AttributeExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/AttributeExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/AttributeExpression.java new file mode 100644 index 0000000..a52929c --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/AttributeExpression.java @@ -0,0 +1,50 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.GroovyCodeVisitor; + + +/** + * Represents an attribute access (accessing the field of a class) such as the expression "foo.@bar". + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class AttributeExpression extends PropertyExpression { + + public AttributeExpression(Expression objectExpression, Expression property) { + super(objectExpression, property, false); + } + + public AttributeExpression(Expression objectExpression, Expression property, boolean safe) { + super(objectExpression, property, safe); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitAttributeExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + AttributeExpression ret = new AttributeExpression(transformer.transform(getObjectExpression()),transformer.transform(getProperty()),isSafe()); + ret.setSourcePosition(this); + ret.setSpreadSafe(isSpreadSafe()); + ret.copyNodeMetaData(this); + return ret; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/BinaryExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/BinaryExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/BinaryExpression.java new file mode 100644 index 0000000..e970947 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/BinaryExpression.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.expr; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.Variable; +import org.codehaus.groovy.syntax.Token; +import org.codehaus.groovy.syntax.Types; + +/** + * Represents two expressions and an operation + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class BinaryExpression extends Expression { + + private Expression leftExpression; + private Expression rightExpression; + private final Token operation; + private boolean safe = false; + + public BinaryExpression(Expression leftExpression, + Token operation, + Expression rightExpression) { + this.leftExpression = leftExpression; + this.operation = operation; + this.rightExpression = rightExpression; + } + + public BinaryExpression(Expression leftExpression, + Token operation, + Expression rightExpression, + boolean safe) { + this(leftExpression, operation, rightExpression); + this.safe = safe; + } + + public String toString() { + return super.toString() + "[" + leftExpression + operation + rightExpression + "]"; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitBinaryExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new BinaryExpression(transformer.transform(leftExpression), operation, transformer.transform(rightExpression), safe); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public Expression getLeftExpression() { + return leftExpression; + } + + public void setLeftExpression(Expression leftExpression) { + this.leftExpression = leftExpression; + } + + public void setRightExpression(Expression rightExpression) { + this.rightExpression = rightExpression; + } + + public Token getOperation() { + return operation; + } + + public Expression getRightExpression() { + return rightExpression; + } + + public String getText() { + if (operation.getType() == Types.LEFT_SQUARE_BRACKET) { + return leftExpression.getText() + (safe ? "?" : "") + "[" + rightExpression.getText() + "]"; + } + return "(" + leftExpression.getText() + " " + operation.getText() + " " + rightExpression.getText() + ")"; + } + + public boolean isSafe() { + return safe; + } + + public void setSafe(boolean safe) { + this.safe = safe; + } + + /** + * Creates an assignment expression in which the specified expression + * is written into the specified variable name. + */ + + public static BinaryExpression newAssignmentExpression(Variable variable, Expression rhs) { + VariableExpression lhs = new VariableExpression(variable); + Token operator = Token.newPlaceholder(Types.ASSIGN); + + return new BinaryExpression(lhs, operator, rhs); + } + + + /** + * Creates variable initialization expression in which the specified expression + * is written into the specified variable name. + */ + + public static BinaryExpression newInitializationExpression(String variable, ClassNode type, Expression rhs) { + VariableExpression lhs = new VariableExpression(variable); + + if (type != null) { + lhs.setType(type); + } + + Token operator = Token.newPlaceholder(Types.ASSIGN); + + return new BinaryExpression(lhs, operator, rhs); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java new file mode 100644 index 0000000..dece59d --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/BitwiseNegationExpression.java @@ -0,0 +1,58 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * @author phk + */ +public class BitwiseNegationExpression extends Expression { + + private final Expression expression; + + public BitwiseNegationExpression(Expression expression) { + this.expression = expression; + } + + public Expression getExpression() { + return expression; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitBitwiseNegationExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new BitwiseNegationExpression(transformer.transform(expression)); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public String getText() { + return expression.getText(); + } + + public ClassNode getType() { + return expression.getType(); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java new file mode 100644 index 0000000..5cab929 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/BooleanExpression.java @@ -0,0 +1,56 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents a boolean expression + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class BooleanExpression extends Expression { + private final Expression expression; + + public BooleanExpression(Expression expression) { + this.expression = expression; + setType(ClassHelper.boolean_TYPE); // for consistency with AsmClassGenerator. see AsmClassGenerator.visitBooleanExpression. + } + + public Expression getExpression() { + return expression; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitBooleanExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new BooleanExpression(transformer.transform(expression)); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public String getText() { + return expression.getText(); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/CastExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/CastExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/CastExpression.java new file mode 100644 index 0000000..f6145bb --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/CastExpression.java @@ -0,0 +1,111 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents a type cast expression + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class CastExpression extends Expression { + + private final Expression expression; + private boolean ignoreAutoboxing=false; + private boolean coerce = false; + private boolean strict = false; + + public static CastExpression asExpression(ClassNode type, Expression expression) { + CastExpression answer = new CastExpression(type, expression); + answer.setCoerce(true); + return answer; + } + + public CastExpression(ClassNode type, Expression expression) { + this(type,expression,false); + } + + public CastExpression(ClassNode type, Expression expression, boolean ignoreAutoboxing) { + super.setType(type); + this.expression = expression; + this.ignoreAutoboxing = ignoreAutoboxing; + } + + public boolean isIgnoringAutoboxing(){ + return ignoreAutoboxing; + } + + public boolean isCoerce() { + return coerce; + } + + public void setCoerce(boolean coerce) { + this.coerce = coerce; + } + + /** + * If strict mode is true, then when the compiler generates a cast, it will disable + * Groovy casts and rely on a strict cast (CHECKCAST) + * @return true if strict mode is enable + */ + public boolean isStrict() { + return strict; + } + + /** + * If strict mode is true, then when the compiler generates a cast, it will disable + * Groovy casts and rely on a strict cast (CHECKCAST) + * @param strict strict mode + */ + public void setStrict(final boolean strict) { + this.strict = strict; + } + + public String toString() { + return super.toString() +"[(" + getType().getName() + ") " + expression + "]"; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitCastExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + CastExpression ret = new CastExpression(getType(), transformer.transform(expression)); + ret.setSourcePosition(this); + ret.setCoerce(this.isCoerce()); + ret.setStrict(this.isStrict()); + ret.copyNodeMetaData(this); + return ret; + } + + public String getText() { + return "(" + getType() + ") " + expression.getText(); + } + + public Expression getExpression() { + return expression; + } + + public void setType(ClassNode t) { + super.setType(t); + } + +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ClassExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ClassExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ClassExpression.java new file mode 100644 index 0000000..9b29e5c --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ClassExpression.java @@ -0,0 +1,51 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents access to a Java/Groovy class in an expression, such + * as when invoking a static method or accessing a static type + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class ClassExpression extends Expression { + + public ClassExpression(ClassNode type) { + super.setType(type); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitClassExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + public String getText() { + return getType().getName(); + } + + public String toString() { + return super.toString() + "[type: " + getType().getName() + "]"; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ClosureExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ClosureExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ClosureExpression.java new file mode 100644 index 0000000..802a6ee --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ClosureExpression.java @@ -0,0 +1,105 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.AstToTextHelper; +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.Parameter; +import org.codehaus.groovy.ast.VariableScope; +import org.codehaus.groovy.ast.stmt.Statement; +import org.codehaus.groovy.runtime.InvokerHelper; + +/** + * Represents a closure expression such as { statement } + * or { i -> statement } or { i, x, String y -> statement } + * + * @author <a href="mailto:[email protected]">James Strachan</a> + * @author Hamlet D'Arcy + */ +public class ClosureExpression extends Expression { + + private final Parameter[] parameters; + private Statement code; + private VariableScope variableScope; + + public ClosureExpression(Parameter[] parameters, Statement code) { + this.parameters = parameters; + this.code = code; + super.setType(ClassHelper.CLOSURE_TYPE.getPlainNodeReference()); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitClosureExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + public String toString() { + return super.toString() + InvokerHelper.toString(parameters) + "{ " + code + " }"; + } + + /** + * This gets the code statement of the closure. You can read this method to find out what actions + * the closure is going to perform. + * + * @return the code statement of the closure + */ + public Statement getCode() { + return code; + } + + /** + * This sets the code statement of the closure. You can use this method in order to add more actions + * during the closure execution. + * + * @param code the new Statement + */ + public void setCode(Statement code) { + this.code = code; + } + + public Parameter[] getParameters() { + return parameters; + } + + public boolean isParameterSpecified() { + return parameters != null && parameters.length > 0; + } + + public VariableScope getVariableScope() { + return variableScope; + } + + public void setVariableScope(VariableScope variableScope) { + this.variableScope = variableScope; + } + + @Override + public String getText() { + String paramText = AstToTextHelper.getParametersText(parameters); + if (paramText.length() > 0) { + return "{ " + paramText + " -> ... }"; + } else { + return "{ -> ... }"; + } + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ClosureListExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ClosureListExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ClosureListExpression.java new file mode 100644 index 0000000..ecbd333 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ClosureListExpression.java @@ -0,0 +1,88 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.ast.VariableScope; + +import java.util.ArrayList; +import java.util.List; + +/** + * This class represents a list of expressions used to + * create closures. Example: + * <code> + * def foo = (1;2;;) + * </code> + * The right side is a ClosureListExpression consisting of + * two ConstantExpressions for the values 1 and 2, and two + * EmptyStatement entries. The ClosureListExpression defines a new + * variable scope. All created Closures share this scope. + * + * + * @author Jochen Theodorou + */ +public class ClosureListExpression extends ListExpression { + + private VariableScope scope; + + public ClosureListExpression(List<Expression> expressions) { + super(expressions); + scope = new VariableScope(); + } + + public ClosureListExpression() { + this(new ArrayList<Expression>(3)); + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitClosureListExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new ClosureListExpression(transformExpressions(getExpressions(), transformer)); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } + + public void setVariableScope(VariableScope scope) { + this.scope = scope; + } + + public VariableScope getVariableScope() { + return scope; + } + + public String getText() { + StringBuilder buffer = new StringBuilder("("); + boolean first = true; + for (Expression expression : getExpressions()) { + if (first) { + first = false; + } else { + buffer.append("; "); + } + + buffer.append(expression.getText()); + } + buffer.append(")"); + return buffer.toString(); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ConstantExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ConstantExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ConstantExpression.java new file mode 100644 index 0000000..dfa2c52 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ConstantExpression.java @@ -0,0 +1,124 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ClassHelper; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents a constant expression such as null, true, false + * + * @author <a href="mailto:[email protected]">James Strachan</a> + */ +public class ConstantExpression extends Expression { + // The following fields are only used internally; every occurrence of a user-defined expression of the same kind + // has its own instance so as to preserve line information. Consequently, to test for such an expression, don't + // compare against the field but call isXXXExpression() instead. + public static final ConstantExpression NULL = new ConstantExpression(null); + public static final ConstantExpression TRUE = new ConstantExpression(Boolean.TRUE); + public static final ConstantExpression FALSE = new ConstantExpression(Boolean.FALSE); + public static final ConstantExpression EMPTY_STRING = new ConstantExpression(""); + public static final ConstantExpression PRIM_TRUE = new ConstantExpression(Boolean.TRUE, true); + public static final ConstantExpression PRIM_FALSE = new ConstantExpression(Boolean.FALSE, true); + //public static final Expression EMPTY_ARRAY = new PropertyExpression(new ClassExpression(ArgumentListExpression.class.getName()), "EMPTY_ARRAY"); + + // the following fields are only used internally; there are no user-defined expressions of the same kind + public static final ConstantExpression VOID = new ConstantExpression(Void.class); + public static final ConstantExpression EMPTY_EXPRESSION = new ConstantExpression(null); + + private final Object value; + private String constantName; + + public ConstantExpression(Object value) { + this(value,false); + } + + public ConstantExpression(Object value, boolean keepPrimitive) { + this.value = value; + if (value != null) { + if (keepPrimitive) { + if (value instanceof Integer) { + setType(ClassHelper.int_TYPE); + } else if (value instanceof Long) { + setType(ClassHelper.long_TYPE); + } else if (value instanceof Boolean) { + setType(ClassHelper.boolean_TYPE); + } else if (value instanceof Double) { + setType(ClassHelper.double_TYPE); + } else if (value instanceof Float) { + setType(ClassHelper.float_TYPE); + } else if (value instanceof Character) { + setType(ClassHelper.char_TYPE); + } else { + setType(ClassHelper.make(value.getClass())); + } + //TODO: more cases here + } else { + setType(ClassHelper.make(value.getClass())); + } + } + } + + public String toString() { + return "ConstantExpression[" + value + "]"; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitConstantExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + return this; + } + + /** + * @return the value of this constant expression + */ + public Object getValue() { + return value; + } + + public String getText() { + return (value == null) ? "null" : value.toString(); + } + + public String getConstantName() { + return constantName; + } + + public void setConstantName(String constantName) { + this.constantName = constantName; + } + + public boolean isNullExpression() { + return value == null; + } + + public boolean isTrueExpression() { + return Boolean.TRUE.equals(value); + } + + public boolean isFalseExpression() { + return Boolean.FALSE.equals(value); + } + + public boolean isEmptyStringExpression() { + return "".equals(value); + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java new file mode 100644 index 0000000..0c040fa --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ConstructorCallExpression.java @@ -0,0 +1,105 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.ASTNode; +import org.codehaus.groovy.ast.ClassNode; +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * A constructor call + * + * @author <a href="mailto:[email protected]">James Strachan</a> + * @author Jochen Theodorou + */ +public class ConstructorCallExpression extends Expression implements MethodCall { + + private final Expression arguments; + private boolean usesAnonymousInnerClass; + + public ConstructorCallExpression(ClassNode type, Expression arguments) { + super.setType(type); + if (!(arguments instanceof TupleExpression)) { + this.arguments = new TupleExpression(arguments); + } else { + this.arguments = arguments; + } + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitConstructorCallExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression args = transformer.transform(arguments); + ConstructorCallExpression ret = new ConstructorCallExpression(getType(), args); + ret.setSourcePosition(this); + ret.setUsingAnonymousInnerClass(isUsingAnonymousInnerClass()); + ret.copyNodeMetaData(this); + return ret; + } + + public ASTNode getReceiver() { + return null; + } + + public String getMethodAsString() { + return "<init>"; + } + + public Expression getArguments() { + return arguments; + } + + public String getText() { + String text = null; + if (isSuperCall()) { + text = "super "; + } else if (isThisCall()) { + text = "this "; + } else { + text = "new " + getType().getName(); + } + return text + arguments.getText(); + } + + public String toString() { + return super.toString() + "[type: " + getType() + " arguments: " + arguments + "]"; + } + + public boolean isSuperCall() { + return getType() == ClassNode.SUPER; + } + + public boolean isSpecialCall() { + return isThisCall() || isSuperCall(); + } + + public boolean isThisCall() { + return getType() == ClassNode.THIS; + } + + public void setUsingAnonymousInnerClass(boolean usage) { + this.usesAnonymousInnerClass = usage; + } + + public boolean isUsingAnonymousInnerClass() { + return usesAnonymousInnerClass; + } +} http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/DeclarationExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/DeclarationExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/DeclarationExpression.java new file mode 100644 index 0000000..6c082a7 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/DeclarationExpression.java @@ -0,0 +1,168 @@ +/* + * 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.expr; + +import org.codehaus.groovy.GroovyBugError; +import org.codehaus.groovy.ast.GroovyCodeVisitor; +import org.codehaus.groovy.syntax.Token; + +/** + * Represents one or more local variables. Typically it is a single local variable + * declared by name with an expression like "def foo" or with type "String foo". However, + * the multiple assignment feature allows you to create two or more variables using + * an expression like: <code>def (x, y) = [1, 2]</code>. + * <p> + * You can access the left hand side of a declaration using the + * "<code>Expression getLeftExpression()</code>" method. In which case you might then + * use <code>instanceof</code> and casting to perform operations specific to a + * single local variable (<code>VariableExpression</code>) or for the multiple + * assignment case (<code>TupleExpression</code>). + * <p> + * Alternatively, if <code>isMultipleAssignmentDeclaration()</code> is <code>false</code> + * you can use the method "<code>VariableExpression getVariableExpression()</code>" method. + * Similarly, if <code>isMultipleAssignmentDeclaration()</code> is <code>true</code> + * you can use the method "<code>TupleExpression getTupleExpression()</code>" method. + * Calling either of these expression getters when the "isMultipleAssignment" condition + * is not appropriate is unsafe and will result in a <code>ClassCastException</code>. + * + * @author Jochen Theodorou + * @author Hamlet D'Arcy + */ +public class DeclarationExpression extends BinaryExpression { + + /** + * Creates a DeclarationExpression for VariableExpressions like "def x" or "String y = 'foo'". + * @param left + * the left hand side of a variable declaration + * @param operation + * the operation, typically an assignment operator + * @param right + * the right hand side of a declaration + */ + public DeclarationExpression(VariableExpression left, Token operation, Expression right) { + super(left,operation,right); + } + + /** + * Creates a DeclarationExpression for Expressions like "def (x, y) = [1, 2]" + * @param left + * the left hand side of a declaration. Must be either a VariableExpression or + * a TupleExpression with at least one element. + * @param operation + * the operation, typically an assignment operator + * @param right + * the right hand side of a declaration + */ + public DeclarationExpression(Expression left, Token operation, Expression right) { + super(left,operation,right); + check(left); + } + + private static void check(Expression left) { + if (left instanceof VariableExpression) { + //nothing + } else if (left instanceof TupleExpression) { + TupleExpression tuple = (TupleExpression) left; + if (tuple.getExpressions().isEmpty()) throw new GroovyBugError("one element required for left side"); + } else { + throw new GroovyBugError("illegal left expression for declaration: "+left); + } + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitDeclarationExpression(this); + } + + /** + * This method returns the left hand side of the declaration cast to the VariableExpression type. + * This is an unsafe method to call. In a multiple assignment statement, the left hand side will + * be a TupleExpression and a ClassCastException will occur. If you invoke this method then + * be sure to invoke isMultipleAssignmentDeclaration() first to check that it is safe to do so. + * If that method returns true then this method is safe to call. + * + * @return left hand side of normal variable declarations + * @throws ClassCastException if the left hand side is not a VariableExpression (and is probably a multiple assignment statement). + */ + public VariableExpression getVariableExpression() { + Expression leftExpression = this.getLeftExpression(); + + return leftExpression instanceof VariableExpression + ? (VariableExpression) leftExpression + : null; + } + + /** + * This method returns the left hand side of the declaration cast to the TupleExpression type. + * This is an unsafe method to call. In a single assignment statement, the left hand side will + * be a VariableExpression and a ClassCastException will occur. If you invoke this method then + * be sure to invoke isMultipleAssignmentDeclaration() first to check that it is safe to do so. + * If that method returns true then this method is safe to call. + * @return + * left hand side of multiple assignment declarations + * @throws ClassCastException + * if the left hand side is not a TupleExpression (and is probably a VariableExpression). + * + */ + public TupleExpression getTupleExpression() { + Expression leftExpression = this.getLeftExpression(); + + return leftExpression instanceof TupleExpression + ? (TupleExpression) leftExpression + : null; + } + + /** + * This method sets the leftExpression for this BinaryExpression. The parameter must be + * either a VariableExpression or a TupleExpression with one or more elements. + * @param leftExpression + * either a VariableExpression or a TupleExpression with one or more elements. + */ + public void setLeftExpression(Expression leftExpression) { + check(leftExpression); + super.setLeftExpression(leftExpression); + } + + public void setRightExpression(Expression rightExpression) { + super.setRightExpression(rightExpression); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new DeclarationExpression(transformer.transform(getLeftExpression()), + getOperation(), transformer.transform(getRightExpression())); + ret.setSourcePosition(this); + ret.addAnnotations(getAnnotations()); + ret.setDeclaringClass(getDeclaringClass()); + ret.copyNodeMetaData(this); + return ret; + } + + /** + * This method tells you if this declaration is a multiple assignment declaration, which + * has the form "def (x, y) = ..." in Groovy. If this method returns true, then the left + * hand side is an ArgumentListExpression. Do not call "getVariableExpression()" on this + * object if this method returns true, instead use "getLeftExpression()". + * @return + * true if this declaration is a multiple assignment declaration, which means the + * left hand side is an ArgumentListExpression. + */ + public boolean isMultipleAssignmentDeclaration() { + return getLeftExpression() instanceof TupleExpression; + } +} + http://git-wip-us.apache.org/repos/asf/groovy/blob/0edfcde9/src/main/java/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java ---------------------------------------------------------------------- diff --git a/src/main/java/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java b/src/main/java/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java new file mode 100644 index 0000000..64df749 --- /dev/null +++ b/src/main/java/org/codehaus/groovy/ast/expr/ElvisOperatorExpression.java @@ -0,0 +1,71 @@ +/* + * 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.expr; + +import org.codehaus.groovy.ast.GroovyCodeVisitor; + +/** + * Represents a short ternary expression x ?: y, which is equal + * to + * <pre> + * def truePart = x + * def booleanPart = truePart as boolean + * booleanPart? truePart : y + * </pre> + * Even if x is no atomic expression, x will be evaluated only + * once. Example: + * <pre class="groovyTestCase"> + * class Foo { + * def index=0 + * def getX(){ index++; return index } + * } + * def foo = new Foo() + * def result = foo.x ?: "false case" + * assert foo.index == 1 + * assert result == 1 + * </pre> + * + * @author <a href="mailto:[email protected]">Jochen Theodorou</a> + * @since 1.5 + */ +public class ElvisOperatorExpression extends TernaryExpression { + + public ElvisOperatorExpression(Expression base, Expression falseExpression) { + super(getBool(base), base, falseExpression); + } + + private static BooleanExpression getBool(Expression base) { + BooleanExpression be = new BooleanExpression(base); + be.setSourcePosition(base); + return be; + } + + public void visit(GroovyCodeVisitor visitor) { + visitor.visitShortTernaryExpression(this); + } + + public Expression transformExpression(ExpressionTransformer transformer) { + Expression ret = new ElvisOperatorExpression( + transformer.transform(getTrueExpression()), + transformer.transform(getFalseExpression())); + ret.setSourcePosition(this); + ret.copyNodeMetaData(this); + return ret; + } +}
