0001: /*
0002: $Id: AsmClassGenerator.java 4598 2006-12-22 20:21:21Z blackdrag $
0003:
0004: Copyright 2003 (C) James Strachan and Bob Mcwhirter. All Rights Reserved.
0005:
0006: Redistribution and use of this software and associated documentation
0007: ("Software"), with or without modification, are permitted provided
0008: that the following conditions are met:
0009:
0010: 1. Redistributions of source code must retain copyright
0011: statements and notices. Redistributions must also contain a
0012: copy of this document.
0013:
0014: 2. Redistributions in binary form must reproduce the
0015: above copyright notice, this list of conditions and the
0016: following disclaimer in the documentation and/or other
0017: materials provided with the distribution.
0018:
0019: 3. The name "groovy" must not be used to endorse or promote
0020: products derived from this Software without prior written
0021: permission of The Codehaus. For written permission,
0022: please contact info@codehaus.org.
0023:
0024: 4. Products derived from this Software may not be called "groovy"
0025: nor may "groovy" appear in their names without prior written
0026: permission of The Codehaus. "groovy" is a registered
0027: trademark of The Codehaus.
0028:
0029: 5. Due credit should be given to The Codehaus -
0030: http://groovy.codehaus.org/
0031:
0032: THIS SOFTWARE IS PROVIDED BY THE CODEHAUS AND CONTRIBUTORS
0033: ``AS IS'' AND ANY EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT
0034: NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
0035: FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL
0036: THE CODEHAUS OR ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
0037: INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
0038: (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
0039: SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
0040: HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
0041: STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
0042: ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
0043: OF THE POSSIBILITY OF SUCH DAMAGE.
0044: */
0045:
0046: package org.codehaus.groovy.classgen;
0047:
0048: import groovy.lang.GroovyObject;
0049: import groovy.lang.GroovyRuntimeException;
0050:
0051: import java.util.ArrayList;
0052: import java.util.Collections;
0053: import java.util.Comparator;
0054: import java.util.HashSet;
0055: import java.util.Iterator;
0056: import java.util.LinkedList;
0057: import java.util.List;
0058: import java.util.Map;
0059: import java.util.Set;
0060: import java.util.logging.Logger;
0061:
0062: import org.codehaus.groovy.GroovyBugError;
0063: import org.codehaus.groovy.ast.ASTNode;
0064: import org.codehaus.groovy.ast.AnnotatedNode;
0065: import org.codehaus.groovy.ast.AnnotationNode;
0066: import org.codehaus.groovy.ast.ClassHelper;
0067: import org.codehaus.groovy.ast.ClassNode;
0068: import org.codehaus.groovy.ast.CompileUnit;
0069: import org.codehaus.groovy.ast.ConstructorNode;
0070: import org.codehaus.groovy.ast.FieldNode;
0071: import org.codehaus.groovy.ast.InnerClassNode;
0072: import org.codehaus.groovy.ast.MethodNode;
0073: import org.codehaus.groovy.ast.Parameter;
0074: import org.codehaus.groovy.ast.PropertyNode;
0075: import org.codehaus.groovy.ast.VariableScope;
0076: import org.codehaus.groovy.ast.expr.ArgumentListExpression;
0077: import org.codehaus.groovy.ast.expr.ArrayExpression;
0078: import org.codehaus.groovy.ast.expr.AttributeExpression;
0079: import org.codehaus.groovy.ast.expr.BinaryExpression;
0080: import org.codehaus.groovy.ast.expr.BitwiseNegExpression;
0081: import org.codehaus.groovy.ast.expr.BooleanExpression;
0082: import org.codehaus.groovy.ast.expr.CastExpression;
0083: import org.codehaus.groovy.ast.expr.ClassExpression;
0084: import org.codehaus.groovy.ast.expr.ClosureExpression;
0085: import org.codehaus.groovy.ast.expr.ConstantExpression;
0086: import org.codehaus.groovy.ast.expr.ConstructorCallExpression;
0087: import org.codehaus.groovy.ast.expr.DeclarationExpression;
0088: import org.codehaus.groovy.ast.expr.Expression;
0089: import org.codehaus.groovy.ast.expr.ExpressionTransformer;
0090: import org.codehaus.groovy.ast.expr.FieldExpression;
0091: import org.codehaus.groovy.ast.expr.GStringExpression;
0092: import org.codehaus.groovy.ast.expr.ListExpression;
0093: import org.codehaus.groovy.ast.expr.MapEntryExpression;
0094: import org.codehaus.groovy.ast.expr.MapExpression;
0095: import org.codehaus.groovy.ast.expr.MethodCallExpression;
0096: import org.codehaus.groovy.ast.expr.MethodPointerExpression;
0097: import org.codehaus.groovy.ast.expr.NegationExpression;
0098: import org.codehaus.groovy.ast.expr.NotExpression;
0099: import org.codehaus.groovy.ast.expr.PostfixExpression;
0100: import org.codehaus.groovy.ast.expr.PrefixExpression;
0101: import org.codehaus.groovy.ast.expr.PropertyExpression;
0102: import org.codehaus.groovy.ast.expr.RangeExpression;
0103: import org.codehaus.groovy.ast.expr.RegexExpression;
0104: import org.codehaus.groovy.ast.expr.SpreadExpression;
0105: import org.codehaus.groovy.ast.expr.SpreadMapExpression;
0106: import org.codehaus.groovy.ast.expr.StaticMethodCallExpression;
0107: import org.codehaus.groovy.ast.expr.TernaryExpression;
0108: import org.codehaus.groovy.ast.expr.TupleExpression;
0109: import org.codehaus.groovy.ast.expr.VariableExpression;
0110: import org.codehaus.groovy.ast.stmt.AssertStatement;
0111: import org.codehaus.groovy.ast.stmt.BlockStatement;
0112: import org.codehaus.groovy.ast.stmt.BreakStatement;
0113: import org.codehaus.groovy.ast.stmt.CaseStatement;
0114: import org.codehaus.groovy.ast.stmt.CatchStatement;
0115: import org.codehaus.groovy.ast.stmt.ContinueStatement;
0116: import org.codehaus.groovy.ast.stmt.DoWhileStatement;
0117: import org.codehaus.groovy.ast.stmt.ExpressionStatement;
0118: import org.codehaus.groovy.ast.stmt.ForStatement;
0119: import org.codehaus.groovy.ast.stmt.IfStatement;
0120: import org.codehaus.groovy.ast.stmt.ReturnStatement;
0121: import org.codehaus.groovy.ast.stmt.Statement;
0122: import org.codehaus.groovy.ast.stmt.SwitchStatement;
0123: import org.codehaus.groovy.ast.stmt.SynchronizedStatement;
0124: import org.codehaus.groovy.ast.stmt.ThrowStatement;
0125: import org.codehaus.groovy.ast.stmt.TryCatchStatement;
0126: import org.codehaus.groovy.ast.stmt.WhileStatement;
0127: import org.codehaus.groovy.control.SourceUnit;
0128: import org.codehaus.groovy.runtime.ScriptBytecodeAdapter;
0129: import org.codehaus.groovy.syntax.RuntimeParserException;
0130: import org.codehaus.groovy.syntax.Types;
0131: import org.objectweb.asm.AnnotationVisitor;
0132: import org.objectweb.asm.ClassVisitor;
0133: import org.objectweb.asm.ClassWriter;
0134: import org.objectweb.asm.Label;
0135: import org.objectweb.asm.MethodVisitor;
0136: import org.objectweb.asm.Opcodes;
0137:
0138: /**
0139: * Generates Java class versions of Groovy classes using ASM.
0140: *
0141: * @author <a href="mailto:james@coredevelopers.net">James Strachan</a>
0142: * @author <a href="mailto:b55r@sina.com">Bing Ran</a>
0143: * @author <a href="mailto:blackdrag@gmx.org">Jochen Theodorou</a>
0144: *
0145: * @version $Revision: 4598 $
0146: */
0147: public class AsmClassGenerator extends ClassGenerator {
0148:
0149: private Logger log = Logger.getLogger(getClass().getName());
0150:
0151: private ClassVisitor cw;
0152: private MethodVisitor cv;
0153: private GeneratorContext context;
0154:
0155: private String sourceFile;
0156:
0157: // current class details
0158: private ClassNode classNode;
0159: private ClassNode outermostClass;
0160: private String internalClassName;
0161: private String internalBaseClassName;
0162:
0163: /** maps the variable names to the JVM indices */
0164: private CompileStack compileStack;
0165:
0166: /** have we output a return statement yet */
0167: private boolean outputReturn;
0168:
0169: /** are we on the left or right of an expression */
0170: private boolean leftHandExpression = false;
0171: /**
0172: * Notes for leftHandExpression:
0173: * The default is false, that menas the right side is default.
0174: * The right side means that variables are read and not written.
0175: * Any change of leftHandExpression to true, should be made carefully.
0176: * If such a change is needed, then it should be set to false as soon as
0177: * possible, but most important in the same method. Setting
0178: * leftHandExpression to false is needed for writing variables.
0179: */
0180:
0181: // method invocation
0182: MethodCallerMultiAdapter invokeMethodOnCurrent = MethodCallerMultiAdapter
0183: .newStatic(ScriptBytecodeAdapter.class,
0184: "invokeMethodOnCurrent", true, false);
0185: MethodCallerMultiAdapter invokeMethodOnSuper = MethodCallerMultiAdapter
0186: .newStatic(ScriptBytecodeAdapter.class,
0187: "invokeMethodOnSuper", true, false);
0188: MethodCallerMultiAdapter invokeMethod = MethodCallerMultiAdapter
0189: .newStatic(ScriptBytecodeAdapter.class, "invokeMethod",
0190: true, false);
0191: MethodCallerMultiAdapter invokeStaticMethod = MethodCallerMultiAdapter
0192: .newStatic(ScriptBytecodeAdapter.class,
0193: "invokeStaticMethod", true, true);
0194: MethodCallerMultiAdapter invokeNew = MethodCallerMultiAdapter
0195: .newStatic(ScriptBytecodeAdapter.class, "invokeNew", true,
0196: true);
0197:
0198: // fields & properties
0199: MethodCallerMultiAdapter setField = MethodCallerMultiAdapter
0200: .newStatic(ScriptBytecodeAdapter.class, "setField", false,
0201: false);
0202: MethodCallerMultiAdapter getField = MethodCallerMultiAdapter
0203: .newStatic(ScriptBytecodeAdapter.class, "getField", false,
0204: false);
0205: MethodCallerMultiAdapter setGroovyObjectField = MethodCallerMultiAdapter
0206: .newStatic(ScriptBytecodeAdapter.class,
0207: "setGroovyObjectField", false, false);
0208: MethodCallerMultiAdapter getGroovyObjectField = MethodCallerMultiAdapter
0209: .newStatic(ScriptBytecodeAdapter.class,
0210: "getGroovyObjectField", false, false);
0211: MethodCallerMultiAdapter setFieldOnSuper = MethodCallerMultiAdapter
0212: .newStatic(ScriptBytecodeAdapter.class, "setFieldOnSuper",
0213: false, false);
0214: MethodCallerMultiAdapter getFieldOnSuper = MethodCallerMultiAdapter
0215: .newStatic(ScriptBytecodeAdapter.class, "getFieldOnSuper",
0216: false, false);
0217:
0218: MethodCallerMultiAdapter setProperty = MethodCallerMultiAdapter
0219: .newStatic(ScriptBytecodeAdapter.class, "setProperty",
0220: false, false);
0221: MethodCallerMultiAdapter getProperty = MethodCallerMultiAdapter
0222: .newStatic(ScriptBytecodeAdapter.class, "getProperty",
0223: false, false);
0224: MethodCallerMultiAdapter setGroovyObjectProperty = MethodCallerMultiAdapter
0225: .newStatic(ScriptBytecodeAdapter.class,
0226: "setGroovyObjectProperty", false, false);
0227: MethodCallerMultiAdapter getGroovyObjectProperty = MethodCallerMultiAdapter
0228: .newStatic(ScriptBytecodeAdapter.class,
0229: "getGroovyObjectProperty", false, false);
0230: MethodCallerMultiAdapter setPropertyOnSuper = MethodCallerMultiAdapter
0231: .newStatic(ScriptBytecodeAdapter.class,
0232: "setPropertyOnSuper", false, false);
0233: MethodCallerMultiAdapter getPropertyOnSuper = MethodCallerMultiAdapter
0234: .newStatic(ScriptBytecodeAdapter.class,
0235: "getPropertyOnSuper", false, false);
0236:
0237: // iterator
0238: MethodCaller iteratorNextMethod = MethodCaller.newInterface(
0239: Iterator.class, "next");
0240: MethodCaller iteratorHasNextMethod = MethodCaller.newInterface(
0241: Iterator.class, "hasNext");
0242: // assert
0243: MethodCaller assertFailedMethod = MethodCaller.newStatic(
0244: ScriptBytecodeAdapter.class, "assertFailed");
0245: // isCase
0246: MethodCaller isCaseMethod = MethodCaller.newStatic(
0247: ScriptBytecodeAdapter.class, "isCase");
0248: //compare
0249: MethodCaller compareIdenticalMethod = MethodCaller.newStatic(
0250: ScriptBytecodeAdapter.class, "compareIdentical");
0251: MethodCaller compareEqualMethod = MethodCaller.newStatic(
0252: ScriptBytecodeAdapter.class, "compareEqual");
0253: MethodCaller compareNotEqualMethod = MethodCaller.newStatic(
0254: ScriptBytecodeAdapter.class, "compareNotEqual");
0255: MethodCaller compareToMethod = MethodCaller.newStatic(
0256: ScriptBytecodeAdapter.class, "compareTo");
0257: MethodCaller compareLessThanMethod = MethodCaller.newStatic(
0258: ScriptBytecodeAdapter.class, "compareLessThan");
0259: MethodCaller compareLessThanEqualMethod = MethodCaller.newStatic(
0260: ScriptBytecodeAdapter.class, "compareLessThanEqual");
0261: MethodCaller compareGreaterThanMethod = MethodCaller.newStatic(
0262: ScriptBytecodeAdapter.class, "compareGreaterThan");
0263: MethodCaller compareGreaterThanEqualMethod = MethodCaller
0264: .newStatic(ScriptBytecodeAdapter.class,
0265: "compareGreaterThanEqual");
0266: //regexpr
0267: MethodCaller findRegexMethod = MethodCaller.newStatic(
0268: ScriptBytecodeAdapter.class, "findRegex");
0269: MethodCaller matchRegexMethod = MethodCaller.newStatic(
0270: ScriptBytecodeAdapter.class, "matchRegex");
0271: MethodCaller regexPattern = MethodCaller.newStatic(
0272: ScriptBytecodeAdapter.class, "regexPattern");
0273: // spread expressions
0274: MethodCaller spreadMap = MethodCaller.newStatic(
0275: ScriptBytecodeAdapter.class, "spreadMap");
0276: MethodCaller despreadList = MethodCaller.newStatic(
0277: ScriptBytecodeAdapter.class, "despreadList");
0278: // Closure
0279: MethodCaller getMethodPointer = MethodCaller.newStatic(
0280: ScriptBytecodeAdapter.class, "getMethodPointer");
0281: MethodCaller invokeClosureMethod = MethodCaller.newStatic(
0282: ScriptBytecodeAdapter.class, "invokeClosure");
0283: //negation
0284: MethodCaller negation = MethodCaller.newStatic(
0285: ScriptBytecodeAdapter.class, "negate");
0286: MethodCaller bitNegation = MethodCaller.newStatic(
0287: ScriptBytecodeAdapter.class, "bitNegate");
0288:
0289: // type converions
0290: MethodCaller asTypeMethod = MethodCaller.newStatic(
0291: ScriptBytecodeAdapter.class, "asType");
0292: MethodCaller castToTypeMethod = MethodCaller.newStatic(
0293: ScriptBytecodeAdapter.class, "castToType");
0294: MethodCaller createListMethod = MethodCaller.newStatic(
0295: ScriptBytecodeAdapter.class, "createList");
0296: MethodCaller createTupleMethod = MethodCaller.newStatic(
0297: ScriptBytecodeAdapter.class, "createTuple");
0298: MethodCaller createMapMethod = MethodCaller.newStatic(
0299: ScriptBytecodeAdapter.class, "createMap");
0300: MethodCaller createRangeMethod = MethodCaller.newStatic(
0301: ScriptBytecodeAdapter.class, "createRange");
0302:
0303: // wrapper creation methods
0304: MethodCaller createPojoWrapperMethod = MethodCaller.newStatic(
0305: ScriptBytecodeAdapter.class, "createPojoWrapper");
0306: MethodCaller createGroovyObjectWrapperMethod = MethodCaller
0307: .newStatic(ScriptBytecodeAdapter.class,
0308: "createGroovyObjectWrapper");
0309:
0310: // constructor calls with this() and super()
0311: MethodCaller selectConstructorAndTransformArguments = MethodCaller
0312: .newStatic(ScriptBytecodeAdapter.class,
0313: "selectConstructorAndTransformArguments");
0314:
0315: // exception blocks list
0316: private List exceptionBlocks = new ArrayList();
0317:
0318: private Set syntheticStaticFields = new HashSet();
0319: private boolean passingClosureParams;
0320:
0321: private ConstructorNode constructorNode;
0322: private MethodNode methodNode;
0323: private BytecodeHelper helper = new BytecodeHelper(null);
0324:
0325: public static final boolean CREATE_DEBUG_INFO = true;
0326: public static final boolean CREATE_LINE_NUMBER_INFO = true;
0327: private static final boolean MARK_START = true;
0328:
0329: public static final boolean ASM_DEBUG = false; // add marker in the bytecode to show source-byecode relationship
0330: private int lineNumber = -1;
0331: private int columnNumber = -1;
0332: private ASTNode currentASTNode = null;
0333:
0334: private DummyClassGenerator dummyGen = null;
0335: private ClassWriter dummyClassWriter = null;
0336:
0337: private ClassNode interfaceClassLoadingClass;
0338:
0339: private boolean implicitThis = false;
0340:
0341: public AsmClassGenerator(GeneratorContext context,
0342: ClassVisitor classVisitor, ClassLoader classLoader,
0343: String sourceFile) {
0344: super (classLoader);
0345: this .context = context;
0346: this .cw = classVisitor;
0347: this .sourceFile = sourceFile;
0348:
0349: this .dummyClassWriter = new ClassWriter(true);
0350: dummyGen = new DummyClassGenerator(context, dummyClassWriter,
0351: classLoader, sourceFile);
0352: compileStack = new CompileStack();
0353:
0354: }
0355:
0356: protected SourceUnit getSourceUnit() {
0357: return null;
0358: }
0359:
0360: // GroovyClassVisitor interface
0361: //-------------------------------------------------------------------------
0362: public void visitClass(ClassNode classNode) {
0363: // todo to be tested
0364: // createDummyClass(classNode);
0365:
0366: try {
0367: syntheticStaticFields.clear();
0368: this .classNode = classNode;
0369: this .outermostClass = null;
0370: this .internalClassName = BytecodeHelper
0371: .getClassInternalName(classNode);
0372:
0373: this .internalBaseClassName = BytecodeHelper
0374: .getClassInternalName(classNode.getSuperClass());
0375:
0376: cw.visit(asmJDKVersion, classNode.getModifiers(),
0377: internalClassName, null, internalBaseClassName,
0378: BytecodeHelper.getClassInternalNames(classNode
0379: .getInterfaces()));
0380: cw.visitSource(sourceFile, null);
0381:
0382: if (classNode.isInterface()) {
0383: ClassNode owner = classNode;
0384: if (owner instanceof InnerClassNode) {
0385: owner = owner.getOuterClass();
0386: }
0387: String outerClassName = owner.getName();
0388: String name = outerClassName + "$"
0389: + context.getNextInnerClassIdx();
0390: interfaceClassLoadingClass = new InnerClassNode(owner,
0391: name, 4128, ClassHelper.OBJECT_TYPE);
0392:
0393: super .visitClass(classNode);
0394: createInterfaceSyntheticStaticFields();
0395: } else {
0396: super .visitClass(classNode);
0397: createMopMethods();
0398: createSyntheticStaticFields();
0399: }
0400:
0401: for (Iterator iter = innerClasses.iterator(); iter
0402: .hasNext();) {
0403: ClassNode innerClass = (ClassNode) iter.next();
0404: String innerClassName = innerClass.getName();
0405: String innerClassInternalName = BytecodeHelper
0406: .getClassInternalName(innerClassName);
0407: {
0408: int index = innerClassName.lastIndexOf('$');
0409: if (index >= 0)
0410: innerClassName = innerClassName
0411: .substring(index + 1);
0412: }
0413: String outerClassName = internalClassName; // default for inner classes
0414: MethodNode enclosingMethod = innerClass
0415: .getEnclosingMethod();
0416: if (enclosingMethod != null) {
0417: // local inner classes do not specify the outer class name
0418: outerClassName = null;
0419: innerClassName = null;
0420: }
0421: cw.visitInnerClass(innerClassInternalName,
0422: outerClassName, innerClassName, innerClass
0423: .getModifiers());
0424: }
0425: //TODO: an inner class should have an entry of itself
0426: cw.visitEnd();
0427: } catch (GroovyRuntimeException e) {
0428: e.setModule(classNode.getModule());
0429: throw e;
0430: }
0431: }
0432:
0433: private void createMopMethods() {
0434: visitMopMethodList(classNode.getMethods(), true);
0435: visitMopMethodList(classNode.getSuperClass()
0436: .getAllDeclaredMethods(), false);
0437: }
0438:
0439: private String[] buildExceptions(ClassNode[] exceptions) {
0440: if (exceptions == null)
0441: return null;
0442: String[] ret = new String[exceptions.length];
0443: for (int i = 0; i < exceptions.length; i++) {
0444: ret[i] = BytecodeHelper.getClassInternalName(exceptions[i]);
0445: }
0446: return ret;
0447: }
0448:
0449: /**
0450: * filters a list of method for MOP methods. For all methods that are no
0451: * MOP methods a MOP method is created if the method is not public and the
0452: * call would be a call on "this" (isThis == true). If the call is not on
0453: * "this", then the call is a call on "super" and all methods are used,
0454: * unless they are already a MOP method
0455: *
0456: * @see #generateMopCalls(LinkedList, boolean)
0457: *
0458: * @param methods unfiltered list of methods for MOP
0459: * @param isThis if true, then we are creating a MOP method on "this", "super" else
0460: */
0461: private void visitMopMethodList(List methods, boolean isThis) {
0462: LinkedList mopCalls = new LinkedList();
0463: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0464: MethodNode mn = (MethodNode) iter.next();
0465: if ((mn.getModifiers() & ACC_ABSTRACT) != 0)
0466: continue;
0467: // no this$ methods for protected/public isThis=true
0468: // super$ method for protected/public isThis=false
0469: // --> results in XOR
0470: if (isThis
0471: ^ (mn.getModifiers() & (ACC_PUBLIC | ACC_PROTECTED)) == 0)
0472: continue;
0473: String methodName = mn.getName();
0474: if (isMopMethod(methodName) || methodName.startsWith("<"))
0475: continue;
0476: String name = getMopMethodName(mn, isThis);
0477: if (containsMethod(methods, name, mn.getParameters()))
0478: continue;
0479: mopCalls.add(mn);
0480: }
0481: generateMopCalls(mopCalls, isThis);
0482: mopCalls.clear();
0483: }
0484:
0485: private boolean containsMethod(List methods, String name,
0486: Parameter[] paras) {
0487: for (Iterator iter = methods.iterator(); iter.hasNext();) {
0488: MethodNode element = (MethodNode) iter.next();
0489: if (element.getName().equals(name)
0490: && equalParameterTypes(paras, element
0491: .getParameters()))
0492: return true;
0493: }
0494: return false;
0495: }
0496:
0497: private boolean equalParameterTypes(Parameter[] p1, Parameter[] p2) {
0498: if (p1.length != p2.length)
0499: return false;
0500: for (int i = 0; i < p1.length; i++) {
0501: if (!p1[i].getType().equals(p2[i].getType()))
0502: return false;
0503: }
0504: return true;
0505: }
0506:
0507: /**
0508: * generates a Meta Object Protocoll method, that is used to call a non public
0509: * method, or to make a call to super.
0510: * @param mopCalls list of methods a mop call method should be generated for
0511: * @param useThis true if "this" should be used for the naming
0512: */
0513: private void generateMopCalls(LinkedList mopCalls, boolean useThis) {
0514: for (Iterator iter = mopCalls.iterator(); iter.hasNext();) {
0515: MethodNode method = (MethodNode) iter.next();
0516: String name = getMopMethodName(method, useThis);
0517: Parameter[] parameters = method.getParameters();
0518: String methodDescriptor = BytecodeHelper
0519: .getMethodDescriptor(method.getReturnType(), method
0520: .getParameters());
0521: cv = cw.visitMethod(Opcodes.ACC_PUBLIC
0522: & Opcodes.ACC_SYNTHETIC, name, methodDescriptor,
0523: null, null);
0524: cv.visitVarInsn(ALOAD, 0);
0525: BytecodeHelper helper = new BytecodeHelper(cv);
0526: int newRegister = 1;
0527: for (int i = 0; i < parameters.length; i++) {
0528: ClassNode type = parameters[i].getType();
0529: helper.load(parameters[i].getType(), newRegister);
0530: // increment to next register, double/long are using two places
0531: newRegister++;
0532: if (type == ClassHelper.double_TYPE
0533: || type == ClassHelper.long_TYPE)
0534: newRegister++;
0535: }
0536: cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper
0537: .getClassInternalName(method.getDeclaringClass()),
0538: method.getName(), methodDescriptor);
0539: helper.doReturn(method.getReturnType());
0540: cv.visitMaxs(0, 0);
0541: cv.visitEnd();
0542: classNode.addMethod(name, Opcodes.ACC_PUBLIC
0543: & Opcodes.ACC_SYNTHETIC, method.getReturnType(),
0544: parameters, null, null);
0545: }
0546: }
0547:
0548: /**
0549: * creates a MOP method name from a method
0550: * @param method the method to be called by the mop method
0551: * @param useThis if true, then it is a call on "this", "super" else
0552: * @return the mop method name
0553: */
0554: public static String getMopMethodName(MethodNode method,
0555: boolean useThis) {
0556: ClassNode declaringNode = method.getDeclaringClass();
0557: int distance = 0;
0558: for (; declaringNode != null; declaringNode = declaringNode
0559: .getSuperClass()) {
0560: distance++;
0561: }
0562: return (useThis ? "this" : "super") + "$" + distance + "$"
0563: + method.getName();
0564: }
0565:
0566: /**
0567: * method to determine if a method is a MOP method. This is done by the
0568: * method name. If the name starts with "this$" or "super$", then it is
0569: * a MOP method
0570: * @param methodName name of the method to test
0571: * @return true if the method is a MOP method
0572: */
0573: public static boolean isMopMethod(String methodName) {
0574: return methodName.startsWith("this$")
0575: || methodName.startsWith("super$");
0576: }
0577:
0578: protected void visitConstructorOrMethod(MethodNode node,
0579: boolean isConstructor) {
0580: String methodType = BytecodeHelper.getMethodDescriptor(node
0581: .getReturnType(), node.getParameters());
0582:
0583: cv = cw
0584: .visitMethod(node.getModifiers(), node.getName(),
0585: methodType, null, buildExceptions(node
0586: .getExceptions()));
0587: helper = new BytecodeHelper(cv);
0588: if (!node.isAbstract()) {
0589: Statement code = node.getCode();
0590: if (isConstructor
0591: && (code == null || !firstStatementIsSpecialConstructorCall(node))) {
0592: // invokes the super class constructor
0593: cv.visitVarInsn(ALOAD, 0);
0594: cv.visitMethodInsn(INVOKESPECIAL,
0595: BytecodeHelper.getClassInternalName(classNode
0596: .getSuperClass()), "<init>", "()V");
0597: }
0598:
0599: compileStack.init(node.getVariableScope(), node
0600: .getParameters(), cv, classNode);
0601:
0602: // ensure we save the current (meta) class in a register
0603: (new ClassExpression(classNode)).visit(this );
0604: cv.visitInsn(POP);
0605: (new ClassExpression(ClassHelper.METACLASS_TYPE))
0606: .visit(this );
0607: cv.visitInsn(POP);
0608:
0609: // handle body
0610: super .visitConstructorOrMethod(node, isConstructor);
0611: if (!outputReturn || node.isVoidMethod()) {
0612: cv.visitInsn(RETURN);
0613: }
0614: compileStack.clear();
0615:
0616: // lets do all the exception blocks
0617: for (Iterator iter = exceptionBlocks.iterator(); iter
0618: .hasNext();) {
0619: Runnable runnable = (Runnable) iter.next();
0620: runnable.run();
0621: }
0622: exceptionBlocks.clear();
0623:
0624: cv.visitMaxs(0, 0);
0625: }
0626: }
0627:
0628: private boolean firstStatementIsSpecialConstructorCall(
0629: MethodNode node) {
0630: Statement code = node.getFirstStatement();
0631: if (code == null || !(code instanceof ExpressionStatement))
0632: return false;
0633:
0634: Expression expression = ((ExpressionStatement) code)
0635: .getExpression();
0636: if (!(expression instanceof ConstructorCallExpression))
0637: return false;
0638: ConstructorCallExpression cce = (ConstructorCallExpression) expression;
0639: return cce.isSpecialCall();
0640: }
0641:
0642: public void visitConstructor(ConstructorNode node) {
0643: this .constructorNode = node;
0644: this .methodNode = null;
0645: outputReturn = false;
0646: super .visitConstructor(node);
0647: }
0648:
0649: public void visitMethod(MethodNode node) {
0650: this .constructorNode = null;
0651: this .methodNode = node;
0652: outputReturn = false;
0653:
0654: super .visitMethod(node);
0655: }
0656:
0657: public void visitField(FieldNode fieldNode) {
0658: onLineNumber(fieldNode, "visitField: " + fieldNode.getName());
0659: ClassNode t = fieldNode.getType();
0660: cw.visitField(fieldNode.getModifiers(), fieldNode.getName(),
0661: BytecodeHelper.getTypeDescription(t), null, //fieldValue, //br all the sudden that one cannot init the field here. init is done in static initilizer and instace intializer.
0662: null);
0663: visitAnnotations(fieldNode);
0664: }
0665:
0666: public void visitProperty(PropertyNode statement) {
0667: // the verifyer created the field and the setter/getter methods, so here is
0668: // not really something to do
0669: onLineNumber(statement, "visitProperty:"
0670: + statement.getField().getName());
0671: this .methodNode = null;
0672: }
0673:
0674: // GroovyCodeVisitor interface
0675: //-------------------------------------------------------------------------
0676:
0677: // Statements
0678: //-------------------------------------------------------------------------
0679:
0680: protected void visitStatement(Statement statement) {
0681: String name = statement.getStatementLabel();
0682: if (name != null) {
0683: Label label = compileStack.createLocalLabel(name);
0684: cv.visitLabel(label);
0685: }
0686: }
0687:
0688: public void visitBlockStatement(BlockStatement block) {
0689: onLineNumber(block, "visitBlockStatement");
0690: visitStatement(block);
0691:
0692: compileStack.pushVariableScope(block.getVariableScope());
0693: super .visitBlockStatement(block);
0694: compileStack.pop();
0695: }
0696:
0697: public void visitForLoop(ForStatement loop) {
0698: onLineNumber(loop, "visitForLoop");
0699: visitStatement(loop);
0700:
0701: compileStack.pushLoop(loop.getVariableScope(), loop
0702: .getStatementLabel());
0703:
0704: //
0705: // Declare the loop counter.
0706: Variable variable = compileStack.defineVariable(loop
0707: .getVariable(), false);
0708:
0709: //
0710: // Then get the iterator and generate the loop control
0711: MethodCallExpression iterator = new MethodCallExpression(loop
0712: .getCollectionExpression(), "iterator",
0713: new ArgumentListExpression());
0714: iterator.visit(this );
0715:
0716: final int iteratorIdx = compileStack.defineTemporaryVariable(
0717: "iterator", ClassHelper.make(java.util.Iterator.class),
0718: true);
0719:
0720: Label continueLabel = compileStack.getContinueLabel();
0721: Label breakLabel = compileStack.getBreakLabel();
0722:
0723: cv.visitLabel(continueLabel);
0724: cv.visitVarInsn(ALOAD, iteratorIdx);
0725: iteratorHasNextMethod.call(cv);
0726: // note: ifeq tests for ==0, a boolean is 0 if it is false
0727: cv.visitJumpInsn(IFEQ, breakLabel);
0728:
0729: cv.visitVarInsn(ALOAD, iteratorIdx);
0730: iteratorNextMethod.call(cv);
0731: helper.storeVar(variable);
0732:
0733: // Generate the loop body
0734: loop.getLoopBlock().visit(this );
0735:
0736: cv.visitJumpInsn(GOTO, continueLabel);
0737: cv.visitLabel(breakLabel);
0738:
0739: compileStack.pop();
0740: }
0741:
0742: public void visitWhileLoop(WhileStatement loop) {
0743: onLineNumber(loop, "visitWhileLoop");
0744: visitStatement(loop);
0745:
0746: compileStack.pushLoop(loop.getStatementLabel());
0747: Label continueLabel = compileStack.getContinueLabel();
0748: Label breakLabel = compileStack.getBreakLabel();
0749:
0750: cv.visitLabel(continueLabel);
0751: loop.getBooleanExpression().visit(this );
0752: cv.visitJumpInsn(IFEQ, breakLabel);
0753:
0754: loop.getLoopBlock().visit(this );
0755:
0756: cv.visitJumpInsn(GOTO, continueLabel);
0757: cv.visitLabel(breakLabel);
0758:
0759: compileStack.pop();
0760: }
0761:
0762: public void visitDoWhileLoop(DoWhileStatement loop) {
0763: onLineNumber(loop, "visitDoWhileLoop");
0764: visitStatement(loop);
0765:
0766: compileStack.pushLoop(loop.getStatementLabel());
0767: Label breakLabel = compileStack.getBreakLabel();
0768: Label continueLabel = compileStack.getContinueLabel();
0769: cv.visitLabel(continueLabel);
0770:
0771: loop.getLoopBlock().visit(this );
0772:
0773: loop.getBooleanExpression().visit(this );
0774: cv.visitJumpInsn(IFEQ, continueLabel);
0775: cv.visitLabel(breakLabel);
0776:
0777: compileStack.pop();
0778: }
0779:
0780: public void visitIfElse(IfStatement ifElse) {
0781: onLineNumber(ifElse, "visitIfElse");
0782: visitStatement(ifElse);
0783: ifElse.getBooleanExpression().visit(this );
0784:
0785: Label l0 = new Label();
0786: cv.visitJumpInsn(IFEQ, l0);
0787:
0788: ifElse.getIfBlock().visit(this );
0789:
0790: Label l1 = new Label();
0791: cv.visitJumpInsn(GOTO, l1);
0792: cv.visitLabel(l0);
0793:
0794: ifElse.getElseBlock().visit(this );
0795: cv.visitLabel(l1);
0796: }
0797:
0798: public void visitTernaryExpression(TernaryExpression expression) {
0799: onLineNumber(expression, "visitTernaryExpression");
0800:
0801: expression.getBooleanExpression().visit(this );
0802:
0803: Label l0 = new Label();
0804: cv.visitJumpInsn(IFEQ, l0);
0805: visitAndAutoboxBoolean(expression.getTrueExpression());
0806:
0807: Label l1 = new Label();
0808: cv.visitJumpInsn(GOTO, l1);
0809: cv.visitLabel(l0);
0810:
0811: visitAndAutoboxBoolean(expression.getFalseExpression());
0812: cv.visitLabel(l1);
0813: }
0814:
0815: public void visitAssertStatement(AssertStatement statement) {
0816: onLineNumber(statement, "visitAssertStatement");
0817: visitStatement(statement);
0818:
0819: BooleanExpression booleanExpression = statement
0820: .getBooleanExpression();
0821: booleanExpression.visit(this );
0822:
0823: Label l0 = new Label();
0824: cv.visitJumpInsn(IFEQ, l0);
0825:
0826: // do nothing
0827:
0828: Label l1 = new Label();
0829: cv.visitJumpInsn(GOTO, l1);
0830: cv.visitLabel(l0);
0831:
0832: // push expression string onto stack
0833: String expressionText = booleanExpression.getText();
0834: List list = new ArrayList();
0835: addVariableNames(booleanExpression, list);
0836: if (list.isEmpty()) {
0837: cv.visitLdcInsn(expressionText);
0838: } else {
0839: boolean first = true;
0840:
0841: // lets create a new expression
0842: cv.visitTypeInsn(NEW, "java/lang/StringBuffer");
0843: cv.visitInsn(DUP);
0844: cv.visitLdcInsn(expressionText + ". Values: ");
0845:
0846: cv.visitMethodInsn(INVOKESPECIAL, "java/lang/StringBuffer",
0847: "<init>", "(Ljava/lang/String;)V");
0848:
0849: int tempIndex = compileStack.defineTemporaryVariable(
0850: "assert", true);
0851:
0852: for (Iterator iter = list.iterator(); iter.hasNext();) {
0853: String name = (String) iter.next();
0854: String text = name + " = ";
0855: if (first) {
0856: first = false;
0857: } else {
0858: text = ", " + text;
0859: }
0860:
0861: cv.visitVarInsn(ALOAD, tempIndex);
0862: cv.visitLdcInsn(text);
0863: cv.visitMethodInsn(INVOKEVIRTUAL,
0864: "java/lang/StringBuffer", "append",
0865: "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
0866: cv.visitInsn(POP);
0867:
0868: cv.visitVarInsn(ALOAD, tempIndex);
0869: new VariableExpression(name).visit(this );
0870: cv.visitMethodInsn(INVOKEVIRTUAL,
0871: "java/lang/StringBuffer", "append",
0872: "(Ljava/lang/Object;)Ljava/lang/StringBuffer;");
0873: cv.visitInsn(POP);
0874:
0875: }
0876: cv.visitVarInsn(ALOAD, tempIndex);
0877: compileStack.removeVar(tempIndex);
0878: }
0879: // now the optional exception expression
0880: statement.getMessageExpression().visit(this );
0881:
0882: assertFailedMethod.call(cv);
0883: cv.visitLabel(l1);
0884: }
0885:
0886: private void addVariableNames(Expression expression, List list) {
0887: if (expression instanceof BooleanExpression) {
0888: BooleanExpression boolExp = (BooleanExpression) expression;
0889: addVariableNames(boolExp.getExpression(), list);
0890: } else if (expression instanceof BinaryExpression) {
0891: BinaryExpression binExp = (BinaryExpression) expression;
0892: addVariableNames(binExp.getLeftExpression(), list);
0893: addVariableNames(binExp.getRightExpression(), list);
0894: } else if (expression instanceof VariableExpression) {
0895: VariableExpression varExp = (VariableExpression) expression;
0896: list.add(varExp.getName());
0897: }
0898: }
0899:
0900: public void visitTryCatchFinally(TryCatchStatement statement) {
0901: onLineNumber(statement, "visitTryCatchFinally");
0902: visitStatement(statement);
0903:
0904: CatchStatement catchStatement = statement.getCatchStatement(0);
0905: Statement tryStatement = statement.getTryStatement();
0906: final Statement finallyStatement = statement
0907: .getFinallyStatement();
0908:
0909: int anyExceptionIndex = compileStack.defineTemporaryVariable(
0910: "exception", false);
0911: if (!finallyStatement.isEmpty()) {
0912: compileStack.pushFinallyBlock(new Runnable() {
0913: public void run() {
0914: finallyStatement.visit(AsmClassGenerator.this );
0915: }
0916: });
0917: }
0918:
0919: // start try block, label needed for exception table
0920: final Label tryStart = new Label();
0921: cv.visitLabel(tryStart);
0922: tryStatement.visit(this );
0923: // goto finally part
0924: final Label finallyStart = new Label();
0925: cv.visitJumpInsn(GOTO, finallyStart);
0926: // marker needed for Exception table
0927: final Label tryEnd = new Label();
0928: cv.visitLabel(tryEnd);
0929:
0930: for (Iterator it = statement.getCatchStatements().iterator(); it
0931: .hasNext();) {
0932: catchStatement = (CatchStatement) it.next();
0933: ClassNode exceptionType = catchStatement.getExceptionType();
0934: // start catch block, label needed for exception table
0935: final Label catchStart = new Label();
0936: cv.visitLabel(catchStart);
0937: // create exception variable and store the exception
0938: compileStack.defineVariable(catchStatement.getVariable(),
0939: true);
0940: // handle catch body
0941: catchStatement.visit(this );
0942: // goto finally start
0943: cv.visitJumpInsn(GOTO, finallyStart);
0944: // add exception to table
0945: final String exceptionTypeInternalName = BytecodeHelper
0946: .getClassInternalName(exceptionType);
0947: exceptionBlocks.add(new Runnable() {
0948: public void run() {
0949: cv.visitTryCatchBlock(tryStart, tryEnd, catchStart,
0950: exceptionTypeInternalName);
0951: }
0952: });
0953: }
0954:
0955: // marker needed for the exception table
0956: final Label endOfAllCatches = new Label();
0957: cv.visitLabel(endOfAllCatches);
0958:
0959: // remove the finally, don't let it visit itself
0960: if (!finallyStatement.isEmpty())
0961: compileStack.popFinallyBlock();
0962:
0963: // start finally
0964: cv.visitLabel(finallyStart);
0965: finallyStatement.visit(this );
0966: // goto end of finally
0967: Label afterFinally = new Label();
0968: cv.visitJumpInsn(GOTO, afterFinally);
0969:
0970: // start a block catching any Exception
0971: final Label catchAny = new Label();
0972: cv.visitLabel(catchAny);
0973: //store exception
0974: cv.visitVarInsn(ASTORE, anyExceptionIndex);
0975: finallyStatement.visit(this );
0976: // load the exception and rethrow it
0977: cv.visitVarInsn(ALOAD, anyExceptionIndex);
0978: cv.visitInsn(ATHROW);
0979:
0980: // end of all catches and finally parts
0981: cv.visitLabel(afterFinally);
0982:
0983: // add catch any block to exception table
0984: exceptionBlocks.add(new Runnable() {
0985: public void run() {
0986: cv.visitTryCatchBlock(tryStart, endOfAllCatches,
0987: catchAny, null);
0988: }
0989: });
0990: }
0991:
0992: public void visitSwitch(SwitchStatement statement) {
0993: onLineNumber(statement, "visitSwitch");
0994: visitStatement(statement);
0995:
0996: statement.getExpression().visit(this );
0997:
0998: // switch does not have a continue label. use its parent's for continue
0999: Label breakLabel = compileStack.pushSwitch();
1000:
1001: int switchVariableIndex = compileStack.defineTemporaryVariable(
1002: "switch", true);
1003:
1004: List caseStatements = statement.getCaseStatements();
1005: int caseCount = caseStatements.size();
1006: Label[] labels = new Label[caseCount + 1];
1007: for (int i = 0; i < caseCount; i++) {
1008: labels[i] = new Label();
1009: }
1010:
1011: int i = 0;
1012: for (Iterator iter = caseStatements.iterator(); iter.hasNext(); i++) {
1013: CaseStatement caseStatement = (CaseStatement) iter.next();
1014: visitCaseStatement(caseStatement, switchVariableIndex,
1015: labels[i], labels[i + 1]);
1016: }
1017:
1018: statement.getDefaultStatement().visit(this );
1019:
1020: cv.visitLabel(breakLabel);
1021:
1022: compileStack.pop();
1023: }
1024:
1025: public void visitCaseStatement(CaseStatement statement) {
1026: }
1027:
1028: public void visitCaseStatement(CaseStatement statement,
1029: int switchVariableIndex, Label this Label, Label nextLabel) {
1030:
1031: onLineNumber(statement, "visitCaseStatement");
1032:
1033: cv.visitVarInsn(ALOAD, switchVariableIndex);
1034: statement.getExpression().visit(this );
1035:
1036: isCaseMethod.call(cv);
1037:
1038: Label l0 = new Label();
1039: cv.visitJumpInsn(IFEQ, l0);
1040:
1041: cv.visitLabel(this Label);
1042:
1043: statement.getCode().visit(this );
1044:
1045: // now if we don't finish with a break we need to jump past
1046: // the next comparison
1047: if (nextLabel != null) {
1048: cv.visitJumpInsn(GOTO, nextLabel);
1049: }
1050:
1051: cv.visitLabel(l0);
1052: }
1053:
1054: public void visitBreakStatement(BreakStatement statement) {
1055: onLineNumber(statement, "visitBreakStatement");
1056: visitStatement(statement);
1057:
1058: String name = statement.getLabel();
1059: Label breakLabel = compileStack.getNamedBreakLabel(name);
1060: compileStack.applyFinallyBlocks(breakLabel, true);
1061:
1062: cv.visitJumpInsn(GOTO, breakLabel);
1063: }
1064:
1065: public void visitContinueStatement(ContinueStatement statement) {
1066: onLineNumber(statement, "visitContinueStatement");
1067: visitStatement(statement);
1068:
1069: String name = statement.getLabel();
1070: Label continueLabel = compileStack.getContinueLabel();
1071: if (name != null)
1072: continueLabel = compileStack.getNamedContinueLabel(name);
1073: compileStack.applyFinallyBlocks(continueLabel, false);
1074: cv.visitJumpInsn(GOTO, continueLabel);
1075: }
1076:
1077: public void visitSynchronizedStatement(
1078: SynchronizedStatement statement) {
1079: onLineNumber(statement, "visitSynchronizedStatement");
1080: visitStatement(statement);
1081:
1082: statement.getExpression().visit(this );
1083: final int index = compileStack.defineTemporaryVariable(
1084: "synchronized", ClassHelper.Integer_TYPE, true);
1085:
1086: final Label synchronizedStart = new Label();
1087: final Label synchronizedEnd = new Label();
1088: final Label catchAll = new Label();
1089:
1090: cv.visitVarInsn(ALOAD, index);
1091: cv.visitInsn(MONITORENTER);
1092: cv.visitLabel(synchronizedStart);
1093:
1094: Runnable finallyPart = new Runnable() {
1095: public void run() {
1096: cv.visitVarInsn(ALOAD, index);
1097: cv.visitInsn(MONITOREXIT);
1098: }
1099: };
1100: compileStack.pushFinallyBlock(finallyPart);
1101: statement.getCode().visit(this );
1102:
1103: finallyPart.run();
1104: cv.visitJumpInsn(GOTO, synchronizedEnd);
1105: cv.visitLabel(catchAll);
1106: finallyPart.run();
1107: cv.visitInsn(ATHROW);
1108: cv.visitLabel(synchronizedEnd);
1109:
1110: compileStack.popFinallyBlock();
1111: exceptionBlocks.add(new Runnable() {
1112: public void run() {
1113: cv.visitTryCatchBlock(synchronizedStart, catchAll,
1114: catchAll, null);
1115: }
1116: });
1117: }
1118:
1119: public void visitThrowStatement(ThrowStatement statement) {
1120: onLineNumber(statement, "visitThrowStatement");
1121: visitStatement(statement);
1122:
1123: statement.getExpression().visit(this );
1124:
1125: // we should infer the type of the exception from the expression
1126: cv.visitTypeInsn(CHECKCAST, "java/lang/Throwable");
1127:
1128: cv.visitInsn(ATHROW);
1129: }
1130:
1131: public void visitReturnStatement(ReturnStatement statement) {
1132: onLineNumber(statement, "visitReturnStatement");
1133: visitStatement(statement);
1134:
1135: ClassNode returnType = methodNode.getReturnType();
1136: if (returnType == ClassHelper.VOID_TYPE) {
1137: if (!(statement == ReturnStatement.RETURN_NULL_OR_VOID)) {
1138: throwException("Cannot use return statement with an expression on a method that returns void");
1139: }
1140: compileStack.applyFinallyBlocks();
1141: cv.visitInsn(RETURN);
1142: outputReturn = true;
1143: return;
1144: }
1145:
1146: Expression expression = statement.getExpression();
1147: evaluateExpression(expression);
1148: if (returnType == ClassHelper.OBJECT_TYPE
1149: && expression.getType() != null
1150: && expression.getType() == ClassHelper.VOID_TYPE) {
1151: cv.visitInsn(ACONST_NULL); // cheat the caller
1152: } else {
1153: // return is based on class type
1154: // we may need to cast
1155: doConvertAndCast(returnType, expression, false, true, false);
1156: helper.unbox(returnType);
1157: }
1158: if (compileStack.hasFinallyBlocks()) {
1159: int returnValueIdx = compileStack.defineTemporaryVariable(
1160: "returnValue", returnType, true);
1161: compileStack.applyFinallyBlocks();
1162: helper.load(returnType, returnValueIdx);
1163: }
1164: helper.doReturn(returnType);
1165: outputReturn = true;
1166: }
1167:
1168: /**
1169: * Casts to the given type unless it can be determined that the cast is unnecessary
1170: */
1171: protected void doConvertAndCast(ClassNode type,
1172: Expression expression, boolean ignoreAutoboxing,
1173: boolean forceCast, boolean coerce) {
1174: ClassNode expType = getExpressionType(expression);
1175: // temp resolution: convert all primitive casting to corresponsing Object type
1176: if (!ignoreAutoboxing && ClassHelper.isPrimitiveType(type)) {
1177: type = ClassHelper.getWrapper(type);
1178: }
1179: if (forceCast || (type != null && !type.equals(expType))) {
1180: doConvertAndCast(type, coerce);
1181: }
1182: }
1183:
1184: /**
1185: * @param expression
1186: */
1187: protected void evaluateExpression(Expression expression) {
1188: visitAndAutoboxBoolean(expression);
1189:
1190: Expression assignExpr = createReturnLHSExpression(expression);
1191: if (assignExpr != null) {
1192: leftHandExpression = false;
1193: assignExpr.visit(this );
1194: }
1195: }
1196:
1197: public void visitExpressionStatement(ExpressionStatement statement) {
1198: onLineNumber(statement, "visitExpressionStatement: "
1199: + statement.getExpression().getClass().getName());
1200: visitStatement(statement);
1201:
1202: Expression expression = statement.getExpression();
1203:
1204: visitAndAutoboxBoolean(expression);
1205:
1206: if (isPopRequired(expression)) {
1207: cv.visitInsn(POP);
1208: }
1209: }
1210:
1211: // Expressions
1212: //-------------------------------------------------------------------------
1213:
1214: public void visitDeclarationExpression(
1215: DeclarationExpression expression) {
1216: onLineNumber(expression, "visitDeclarationExpression: \""
1217: + expression.getVariableExpression().getName() + "\"");
1218:
1219: Expression rightExpression = expression.getRightExpression();
1220: // no need to visit left side, just get the variable name
1221: VariableExpression vex = expression.getVariableExpression();
1222: ClassNode type = vex.getType();
1223:
1224: // lets not cast for primitive types as we handle these in field setting etc
1225: if (ClassHelper.isPrimitiveType(type)) {
1226: rightExpression.visit(this );
1227: } else {
1228: if (type != ClassHelper.OBJECT_TYPE) {
1229: visitCastExpression(new CastExpression(type,
1230: rightExpression));
1231: } else {
1232: visitAndAutoboxBoolean(rightExpression);
1233: }
1234: }
1235: compileStack.defineVariable(vex, true);
1236: }
1237:
1238: public void visitBinaryExpression(BinaryExpression expression) {
1239: onLineNumber(expression, "visitBinaryExpression: \""
1240: + expression.getOperation().getText() + "\" ");
1241: switch (expression.getOperation().getType()) {
1242: case Types.EQUAL: // = assignment
1243: evaluateEqual(expression);
1244: break;
1245:
1246: case Types.COMPARE_IDENTICAL: // ===
1247: evaluateBinaryExpression(compareIdenticalMethod, expression);
1248: break;
1249:
1250: case Types.COMPARE_EQUAL: // ==
1251: evaluateBinaryExpression(compareEqualMethod, expression);
1252: break;
1253:
1254: case Types.COMPARE_NOT_EQUAL:
1255: evaluateBinaryExpression(compareNotEqualMethod, expression);
1256: break;
1257:
1258: case Types.COMPARE_TO:
1259: evaluateCompareTo(expression);
1260: break;
1261:
1262: case Types.COMPARE_GREATER_THAN:
1263: evaluateBinaryExpression(compareGreaterThanMethod,
1264: expression);
1265: break;
1266:
1267: case Types.COMPARE_GREATER_THAN_EQUAL:
1268: evaluateBinaryExpression(compareGreaterThanEqualMethod,
1269: expression);
1270: break;
1271:
1272: case Types.COMPARE_LESS_THAN:
1273: evaluateBinaryExpression(compareLessThanMethod, expression);
1274: break;
1275:
1276: case Types.COMPARE_LESS_THAN_EQUAL:
1277: evaluateBinaryExpression(compareLessThanEqualMethod,
1278: expression);
1279: break;
1280:
1281: case Types.LOGICAL_AND:
1282: evaluateLogicalAndExpression(expression);
1283: break;
1284:
1285: case Types.LOGICAL_OR:
1286: evaluateLogicalOrExpression(expression);
1287: break;
1288:
1289: case Types.BITWISE_AND:
1290: evaluateBinaryExpression("and", expression);
1291: break;
1292:
1293: case Types.BITWISE_AND_EQUAL:
1294: evaluateBinaryExpressionWithAsignment("and", expression);
1295: break;
1296:
1297: case Types.BITWISE_OR:
1298: evaluateBinaryExpression("or", expression);
1299: break;
1300:
1301: case Types.BITWISE_OR_EQUAL:
1302: evaluateBinaryExpressionWithAsignment("or", expression);
1303: break;
1304:
1305: case Types.BITWISE_XOR:
1306: evaluateBinaryExpression("xor", expression);
1307: break;
1308:
1309: case Types.BITWISE_XOR_EQUAL:
1310: evaluateBinaryExpressionWithAsignment("xor", expression);
1311: break;
1312:
1313: case Types.PLUS:
1314: evaluateBinaryExpression("plus", expression);
1315: break;
1316:
1317: case Types.PLUS_EQUAL:
1318: evaluateBinaryExpressionWithAsignment("plus", expression);
1319: break;
1320:
1321: case Types.MINUS:
1322: evaluateBinaryExpression("minus", expression);
1323: break;
1324:
1325: case Types.MINUS_EQUAL:
1326: evaluateBinaryExpressionWithAsignment("minus", expression);
1327: break;
1328:
1329: case Types.MULTIPLY:
1330: evaluateBinaryExpression("multiply", expression);
1331: break;
1332:
1333: case Types.MULTIPLY_EQUAL:
1334: evaluateBinaryExpressionWithAsignment("multiply",
1335: expression);
1336: break;
1337:
1338: case Types.DIVIDE:
1339: evaluateBinaryExpression("div", expression);
1340: break;
1341:
1342: case Types.DIVIDE_EQUAL:
1343: //SPG don't use divide since BigInteger implements directly
1344: //and we want to dispatch through DefaultGroovyMethods to get a BigDecimal result
1345: evaluateBinaryExpressionWithAsignment("div", expression);
1346: break;
1347:
1348: case Types.INTDIV:
1349: evaluateBinaryExpression("intdiv", expression);
1350: break;
1351:
1352: case Types.INTDIV_EQUAL:
1353: evaluateBinaryExpressionWithAsignment("intdiv", expression);
1354: break;
1355:
1356: case Types.MOD:
1357: evaluateBinaryExpression("mod", expression);
1358: break;
1359:
1360: case Types.MOD_EQUAL:
1361: evaluateBinaryExpressionWithAsignment("mod", expression);
1362: break;
1363:
1364: case Types.POWER:
1365: evaluateBinaryExpression("power", expression);
1366: break;
1367:
1368: case Types.POWER_EQUAL:
1369: evaluateBinaryExpressionWithAsignment("power", expression);
1370: break;
1371:
1372: case Types.LEFT_SHIFT:
1373: evaluateBinaryExpression("leftShift", expression);
1374: break;
1375:
1376: case Types.LEFT_SHIFT_EQUAL:
1377: evaluateBinaryExpressionWithAsignment("leftShift",
1378: expression);
1379: break;
1380:
1381: case Types.RIGHT_SHIFT:
1382: evaluateBinaryExpression("rightShift", expression);
1383: break;
1384:
1385: case Types.RIGHT_SHIFT_EQUAL:
1386: evaluateBinaryExpressionWithAsignment("rightShift",
1387: expression);
1388: break;
1389:
1390: case Types.RIGHT_SHIFT_UNSIGNED:
1391: evaluateBinaryExpression("rightShiftUnsigned", expression);
1392: break;
1393:
1394: case Types.RIGHT_SHIFT_UNSIGNED_EQUAL:
1395: evaluateBinaryExpressionWithAsignment("rightShiftUnsigned",
1396: expression);
1397: break;
1398:
1399: case Types.KEYWORD_INSTANCEOF:
1400: evaluateInstanceof(expression);
1401: break;
1402:
1403: case Types.FIND_REGEX:
1404: evaluateBinaryExpression(findRegexMethod, expression);
1405: break;
1406:
1407: case Types.MATCH_REGEX:
1408: evaluateBinaryExpression(matchRegexMethod, expression);
1409: break;
1410:
1411: case Types.LEFT_SQUARE_BRACKET:
1412: if (leftHandExpression) {
1413: throwException("Should not be called here. Possible reason: postfix operation on array.");
1414: // This is handled right now in the evaluateEqual()
1415: // should support this here later
1416: //evaluateBinaryExpression("putAt", expression);
1417: } else {
1418: evaluateBinaryExpression("getAt", expression);
1419: }
1420: break;
1421:
1422: case Types.KEYWORD_IN:
1423: evaluateBinaryExpression(isCaseMethod, expression);
1424: break;
1425:
1426: default:
1427: throwException("Operation: " + expression.getOperation()
1428: + " not supported");
1429: }
1430: }
1431:
1432: private void load(Expression exp) {
1433:
1434: boolean wasLeft = leftHandExpression;
1435: leftHandExpression = false;
1436: // if (CREATE_DEBUG_INFO)
1437: // helper.mark("-- loading expression: " + exp.getClass().getName() +
1438: // " at [" + exp.getLineNumber() + ":" + exp.getColumnNumber() + "]");
1439: //exp.visit(this);
1440: visitAndAutoboxBoolean(exp);
1441: // if (CREATE_DEBUG_INFO)
1442: // helper.mark(" -- end of loading --");
1443:
1444: leftHandExpression = wasLeft;
1445: }
1446:
1447: public void visitPostfixExpression(PostfixExpression expression) {
1448: switch (expression.getOperation().getType()) {
1449: case Types.PLUS_PLUS:
1450: evaluatePostfixMethod("next", expression.getExpression());
1451: break;
1452: case Types.MINUS_MINUS:
1453: evaluatePostfixMethod("previous", expression
1454: .getExpression());
1455: break;
1456: }
1457: }
1458:
1459: private void throwException(String s) {
1460: throw new RuntimeParserException(s, currentASTNode);
1461: }
1462:
1463: public void visitPrefixExpression(PrefixExpression expression) {
1464: switch (expression.getOperation().getType()) {
1465: case Types.PLUS_PLUS:
1466: evaluatePrefixMethod("next", expression.getExpression());
1467: break;
1468: case Types.MINUS_MINUS:
1469: evaluatePrefixMethod("previous", expression.getExpression());
1470: break;
1471: }
1472: }
1473:
1474: public void visitClosureExpression(ClosureExpression expression) {
1475: ClassNode innerClass = createClosureClass(expression);
1476: addInnerClass(innerClass);
1477: String innerClassinternalName = BytecodeHelper
1478: .getClassInternalName(innerClass);
1479:
1480: passingClosureParams = true;
1481: List constructors = innerClass.getDeclaredConstructors();
1482: ConstructorNode node = (ConstructorNode) constructors.get(0);
1483:
1484: Parameter[] localVariableParams = node.getParameters();
1485:
1486: cv.visitTypeInsn(NEW, innerClassinternalName);
1487: cv.visitInsn(DUP);
1488: if (isStaticMethod() || classNode.isStaticClass()) {
1489: visitClassExpression(new ClassExpression(classNode));
1490: visitClassExpression(new ClassExpression(
1491: getOutermostClass()));
1492: } else {
1493: cv.visitVarInsn(ALOAD, 0);
1494: loadThis();
1495: }
1496:
1497: // now lets load the various parameters we're passing
1498: // we start at index 1 because the first variable we pass
1499: // is the owner instance and at this point it is already
1500: // on the stack
1501: for (int i = 2; i < localVariableParams.length; i++) {
1502: Parameter param = localVariableParams[i];
1503: String name = param.getName();
1504:
1505: // compileStack.containsVariable(name) means to ask if the variable is already declared
1506: // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a field
1507: // If it is no field and is not yet declared, then it is either a closure shared variable or
1508: // an already declared variable.
1509: if (!compileStack.containsVariable(name)
1510: && compileStack.getScope()
1511: .isReferencedClassVariable(name)) {
1512: visitFieldExpression(new FieldExpression(classNode
1513: .getField(name)));
1514: } else {
1515: Variable v = compileStack.getVariable(name, classNode
1516: .getSuperClass() != ClassHelper.CLOSURE_TYPE);
1517: if (v == null) {
1518: // variable is not on stack because we are
1519: // inside a nested Closure and this variable
1520: // was not used before
1521: // then load it from the Closure field
1522: FieldNode field = classNode.getField(name);
1523: cv.visitVarInsn(ALOAD, 0);
1524: cv.visitFieldInsn(GETFIELD, internalClassName,
1525: name,
1526: BytecodeHelper.getTypeDescription(field
1527: .getType()));
1528: // and define it
1529: // Note:
1530: // we can simply define it here and don't have to
1531: // be afraid about name problems because a second
1532: // variable with that name is not allowed inside the closure
1533: param.setClosureSharedVariable(false);
1534: v = compileStack.defineVariable(param, true);
1535: param.setClosureSharedVariable(true);
1536: v.setHolder(true);
1537: }
1538: cv.visitVarInsn(ALOAD, v.getIndex());
1539: }
1540: }
1541: passingClosureParams = false;
1542:
1543: // we may need to pass in some other constructors
1544: //cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V");
1545: cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName,
1546: "<init>", BytecodeHelper.getMethodDescriptor(
1547: ClassHelper.VOID_TYPE, localVariableParams));
1548: }
1549:
1550: /**
1551: * Loads either this object or if we're inside a closure then load the top level owner
1552: */
1553: protected void loadThisOrOwner() {
1554: if (isInnerClass()) {
1555: visitFieldExpression(new FieldExpression(classNode
1556: .getField("owner")));
1557: } else {
1558: loadThis();
1559: }
1560: }
1561:
1562: public void visitRegexExpression(RegexExpression expression) {
1563: expression.getRegex().visit(this );
1564: regexPattern.call(cv);
1565: }
1566:
1567: /**
1568: * Generate byte code for constants
1569: * @see <a href="http://java.sun.com/docs/books/vmspec/2nd-edition/html/ClassFile.doc.html#14152">Class field types</a>
1570: */
1571: public void visitConstantExpression(ConstantExpression expression) {
1572: Object value = expression.getValue();
1573: helper.loadConstant(value);
1574: }
1575:
1576: public void visitSpreadExpression(SpreadExpression expression) {
1577: throw new GroovyBugError(
1578: "SpreadExpression should not be visited here");
1579: }
1580:
1581: public void visitSpreadMapExpression(SpreadMapExpression expression) {
1582: Expression subExpression = expression.getExpression();
1583: subExpression.visit(this );
1584: spreadMap.call(cv);
1585: }
1586:
1587: public void visitMethodPointerExpression(
1588: MethodPointerExpression expression) {
1589: Expression subExpression = expression.getExpression();
1590: subExpression.visit(this );
1591: helper.loadConstant(expression.getMethodName());
1592: getMethodPointer.call(cv);
1593: }
1594:
1595: public void visitNegationExpression(NegationExpression expression) {
1596: Expression subExpression = expression.getExpression();
1597: subExpression.visit(this );
1598: negation.call(cv);
1599: }
1600:
1601: public void visitBitwiseNegExpression(
1602: BitwiseNegExpression expression) {
1603: Expression subExpression = expression.getExpression();
1604: subExpression.visit(this );
1605: bitNegation.call(cv);
1606: }
1607:
1608: public void visitCastExpression(CastExpression expression) {
1609: ClassNode type = expression.getType();
1610: visitAndAutoboxBoolean(expression.getExpression());
1611: doConvertAndCast(type, expression.getExpression(), expression
1612: .isIgnoringAutoboxing(), false, expression.isCoerce());
1613: }
1614:
1615: public void visitNotExpression(NotExpression expression) {
1616: Expression subExpression = expression.getExpression();
1617: subExpression.visit(this );
1618: // if we do !object, then the cast to boolean will
1619: // do the conversion of Object to boolean. so a simple
1620: // call to unbox is enough here.
1621: if (!isComparisonExpression(subExpression)
1622: && !(subExpression instanceof BooleanExpression)) {
1623: helper.unbox(boolean.class);
1624: }
1625: helper.negateBoolean();
1626: }
1627:
1628: /**
1629: * return a primitive boolean value of the BooleanExpresion.
1630: * @param expression
1631: */
1632: public void visitBooleanExpression(BooleanExpression expression) {
1633: compileStack.pushBooleanExpression();
1634: expression.getExpression().visit(this );
1635:
1636: if (!isComparisonExpression(expression.getExpression())) {
1637: // comment out for optimization when boolean values are not autoboxed for eg. function calls.
1638: // Class typeClass = expression.getExpression().getTypeClass();
1639: // if (typeClass != null && typeClass != boolean.class) {
1640: helper.unbox(boolean.class); // to return a primitive boolean
1641: // }
1642: }
1643: compileStack.pop();
1644: }
1645:
1646: private void makeInvokeMethodCall(MethodCallExpression call,
1647: boolean useSuper, MethodCallerMultiAdapter adapter) {
1648: // receiver
1649: // we operate on GroovyObject if possible
1650: Expression objectExpression = call.getObjectExpression();
1651: if (!isStaticMethod() && !isStaticContext()
1652: && isThisExpression(call.getObjectExpression())) {
1653: objectExpression = new CastExpression(ClassHelper
1654: .make(GroovyObject.class), objectExpression);
1655: }
1656: // message name
1657: Expression messageName = new CastExpression(
1658: ClassHelper.STRING_TYPE, call.getMethod());
1659: if (useSuper) {
1660: makeCall(new ClassExpression(getOutermostClass()
1661: .getSuperClass()), objectExpression, messageName,
1662: call.getArguments(), adapter, call.isSafe(), call
1663: .isSpreadSafe(), false);
1664: } else {
1665: makeCall(objectExpression, messageName,
1666: call.getArguments(), adapter, call.isSafe(), call
1667: .isSpreadSafe(), call.isImplicitThis());
1668: }
1669: }
1670:
1671: private void makeCall(Expression receiver, Expression message,
1672: Expression arguments, MethodCallerMultiAdapter adapter,
1673: boolean safe, boolean spreadSafe, boolean implicitThis) {
1674: ClassNode cn = classNode;
1675: if (isInClosure() && !implicitThis) {
1676: cn = getOutermostClass();
1677: }
1678: makeCall(new ClassExpression(cn), receiver, message, arguments,
1679: adapter, safe, spreadSafe, implicitThis);
1680: }
1681:
1682: private void makeCall(ClassExpression sender, Expression receiver,
1683: Expression message, Expression arguments,
1684: MethodCallerMultiAdapter adapter, boolean safe,
1685: boolean spreadSafe, boolean implicitThis) {
1686: // ensure VariableArguments are read, not stored
1687: boolean lhs = leftHandExpression;
1688: leftHandExpression = false;
1689:
1690: // sender
1691: sender.visit(this );
1692: // receiver
1693: boolean oldVal = this .implicitThis;
1694: this .implicitThis = implicitThis;
1695: receiver.visit(this );
1696: this .implicitThis = oldVal;
1697: // message
1698: if (message != null)
1699: message.visit(this );
1700:
1701: // arguments
1702: boolean containsSpreadExpression = containsSpreadExpression(arguments);
1703: int numberOfArguments = containsSpreadExpression ? -1
1704: : argumentSize(arguments);
1705: if (numberOfArguments > adapter.maxArgs
1706: || containsSpreadExpression) {
1707: ArgumentListExpression ae;
1708: if (arguments instanceof ArgumentListExpression) {
1709: ae = (ArgumentListExpression) arguments;
1710: } else if (arguments instanceof TupleExpression) {
1711: TupleExpression te = (TupleExpression) arguments;
1712: ae = new ArgumentListExpression(te.getExpressions());
1713: } else {
1714: ae = new ArgumentListExpression();
1715: ae.addExpression(arguments);
1716: }
1717: if (containsSpreadExpression) {
1718: despreadList(ae.getExpressions(), true);
1719: } else {
1720: ae.visit(this );
1721: }
1722: } else if (numberOfArguments > 0) {
1723: TupleExpression te = (TupleExpression) arguments;
1724: for (int i = 0; i < numberOfArguments; i++) {
1725: Expression argument = te.getExpression(i);
1726: visitAndAutoboxBoolean(argument);
1727: if (argument instanceof CastExpression)
1728: loadWrapper(argument);
1729: }
1730: }
1731:
1732: adapter.call(cv, numberOfArguments, safe, spreadSafe);
1733:
1734: leftHandExpression = lhs;
1735: }
1736:
1737: private void despreadList(List expressions, boolean wrap) {
1738:
1739: ArrayList spreadIndexes = new ArrayList();
1740: ArrayList spreadExpressions = new ArrayList();
1741: ArrayList normalArguments = new ArrayList();
1742: for (int i = 0; i < expressions.size(); i++) {
1743: Object expr = expressions.get(i);
1744: if (!(expr instanceof SpreadExpression)) {
1745: normalArguments.add(expr);
1746: } else {
1747: spreadIndexes.add(new ConstantExpression(new Integer(i
1748: - spreadExpressions.size())));
1749: spreadExpressions.add(((SpreadExpression) expr)
1750: .getExpression());
1751: }
1752: }
1753:
1754: //load normal arguments as array
1755: visitTupleExpression(
1756: new ArgumentListExpression(normalArguments), wrap);
1757: //load spread expressions as array
1758: (new TupleExpression(spreadExpressions)).visit(this );
1759: //load insertion index
1760: (new ArrayExpression(ClassHelper.int_TYPE, spreadIndexes, null))
1761: .visit(this );
1762: despreadList.call(cv);
1763: }
1764:
1765: public void visitMethodCallExpression(MethodCallExpression call) {
1766: onLineNumber(call, "visitMethodCallExpression: \""
1767: + call.getMethod() + "\":");
1768:
1769: Expression arguments = call.getArguments();
1770: String methodName = call.getMethodAsString();
1771: boolean isSuperMethodCall = usesSuper(call);
1772: boolean isThisExpression = isThisExpression(call
1773: .getObjectExpression());
1774:
1775: // are we a local variable
1776: if (methodName != null && isThisExpression
1777: && isFieldOrVariable(methodName)
1778: && !classNode.hasPossibleMethod(methodName, arguments)) {
1779: // lets invoke the closure method
1780: visitVariableExpression(new VariableExpression(methodName));
1781: arguments.visit(this );
1782: invokeClosureMethod.call(cv);
1783: } else {
1784: MethodCallerMultiAdapter adapter = invokeMethod;
1785: if (isThisExpression)
1786: adapter = invokeMethodOnCurrent;
1787: if (isSuperMethodCall)
1788: adapter = invokeMethodOnSuper;
1789: if (isStaticInvocation(call))
1790: adapter = invokeStaticMethod;
1791: makeInvokeMethodCall(call, isSuperMethodCall, adapter);
1792: }
1793: }
1794:
1795: private boolean isStaticInvocation(MethodCallExpression call) {
1796: if (!isThisExpression(call.getObjectExpression()))
1797: return false;
1798: if (isStaticMethod())
1799: return true;
1800: return isStaticContext() && !call.isImplicitThis();
1801: }
1802:
1803: protected boolean emptyArguments(Expression arguments) {
1804: return argumentSize(arguments) == 0;
1805: }
1806:
1807: protected static boolean containsSpreadExpression(
1808: Expression arguments) {
1809: List args = null;
1810: if (arguments instanceof TupleExpression) {
1811: TupleExpression tupleExpression = (TupleExpression) arguments;
1812: args = tupleExpression.getExpressions();
1813: } else if (arguments instanceof ListExpression) {
1814: ListExpression le = (ListExpression) arguments;
1815: args = le.getExpressions();
1816: } else {
1817: return arguments instanceof SpreadExpression;
1818: }
1819: for (Iterator iter = args.iterator(); iter.hasNext();) {
1820: if (iter.next() instanceof SpreadExpression)
1821: return true;
1822: }
1823: return false;
1824: }
1825:
1826: protected static int argumentSize(Expression arguments) {
1827: if (arguments instanceof TupleExpression) {
1828: TupleExpression tupleExpression = (TupleExpression) arguments;
1829: int size = tupleExpression.getExpressions().size();
1830: return size;
1831: }
1832: return 1;
1833: }
1834:
1835: public void visitStaticMethodCallExpression(
1836: StaticMethodCallExpression call) {
1837: onLineNumber(call, "visitStaticMethodCallExpression: \""
1838: + call.getMethod() + "\":");
1839:
1840: makeCall(new ClassExpression(call.getOwnerType()),
1841: new ConstantExpression(call.getMethod()), call
1842: .getArguments(), invokeStaticMethod, false,
1843: false, false);
1844: }
1845:
1846: private void visitSpecialConstructorCall(
1847: ConstructorCallExpression call) {
1848: ClassNode callNode = classNode;
1849: if (call.isSuperCall())
1850: callNode = callNode.getSuperClass();
1851: List constructors = sortConstructors(call, callNode);
1852: call.getArguments().visit(this );
1853: // keep Objet[] on stack
1854: cv.visitInsn(DUP);
1855: // to select the constructor we need also the number of
1856: // available constructors and the class we want to make
1857: // the call on
1858: helper.pushConstant(constructors.size());
1859: visitClassExpression(new ClassExpression(callNode));
1860: // removes one Object[] leaves the int containing the
1861: // call flags and the construtcor number
1862: selectConstructorAndTransformArguments.call(cv);
1863: // Object[],int -> int,Object[],int
1864: // we need to examine the flags and maybe change the
1865: // Object[] later, so this reordering will do the job
1866: cv.visitInsn(DUP_X1);
1867: // test if rewrap flag is set
1868: cv.visitInsn(ICONST_1);
1869: cv.visitInsn(IAND);
1870: Label afterIf = new Label();
1871: cv.visitJumpInsn(IFEQ, afterIf);
1872: // true part, so rewrap using the first argument
1873: cv.visitInsn(ICONST_0);
1874: cv.visitInsn(AALOAD);
1875: cv.visitTypeInsn(CHECKCAST, "[Ljava/lang/Object;");
1876: cv.visitLabel(afterIf);
1877: // here the stack is int,Object[], but we need the
1878: // the int for our table, so swap it
1879: cv.visitInsn(SWAP);
1880: //load "this"
1881: cv.visitVarInsn(ALOAD, 0);
1882: cv.visitInsn(SWAP);
1883: //prepare switch with >>8
1884: cv.visitIntInsn(BIPUSH, 8);
1885: cv.visitInsn(ISHR);
1886: Label[] targets = new Label[constructors.size()];
1887: int[] indices = new int[constructors.size()];
1888: for (int i = 0; i < targets.length; i++) {
1889: targets[i] = new Label();
1890: indices[i] = i;
1891: }
1892: // create switch targets
1893: Label defaultLabel = new Label();
1894: Label afterSwitch = new Label();
1895: cv.visitLookupSwitchInsn(defaultLabel, indices, targets);
1896: for (int i = 0; i < targets.length; i++) {
1897: cv.visitLabel(targets[i]);
1898: // to keep the stack height, we need to leave
1899: // one Object[] on the stack as last element. At the
1900: // same time, we need the Object[] on top of the stack
1901: // to extract the parameters. So a SWAP will exchange
1902: // "this" and Object[], a DUP_X1 will then copy the Object[]
1903: /// to the last place in the stack:
1904: // Object[],this -SWAP-> this,Object[]
1905: // this,Object[] -DUP_X1-> Object[],this,Object[]
1906: cv.visitInsn(SWAP);
1907: cv.visitInsn(DUP_X1);
1908:
1909: ConstructorNode cn = (ConstructorNode) constructors.get(i);
1910: String descriptor = helper.getMethodDescriptor(
1911: ClassHelper.VOID_TYPE, cn.getParameters());
1912: // unwrap the Object[] and make transformations if needed
1913: // that means, to duplicate the Object[], make a cast with possible
1914: // unboxing and then swap it with the Object[] for each parameter
1915: Parameter[] parameters = cn.getParameters();
1916: for (int p = 0; p < parameters.length; p++) {
1917: cv.visitInsn(DUP);
1918: helper.pushConstant(p);
1919: cv.visitInsn(AALOAD);
1920: ClassNode type = parameters[p].getType();
1921: if (ClassHelper.isPrimitiveType(type)) {
1922: helper.unbox(type);
1923: } else {
1924: helper.doCast(type);
1925: }
1926: helper.swapWithObject(type);
1927: }
1928: // at the end we remove the Object[]
1929: cv.visitInsn(POP);
1930: // make the constructor call
1931: cv.visitMethodInsn(INVOKESPECIAL, BytecodeHelper
1932: .getClassInternalName(callNode), "<init>",
1933: descriptor);
1934: cv.visitJumpInsn(GOTO, afterSwitch);
1935: }
1936: cv.visitLabel(defaultLabel);
1937: // this part should never be reached!
1938: cv.visitTypeInsn(NEW, "java/lang/IllegalArgumentException");
1939: cv.visitInsn(DUP);
1940: cv.visitLdcInsn("illegal constructor number");
1941: cv.visitMethodInsn(INVOKESPECIAL,
1942: "java/lang/IllegalArgumentException", "<init>",
1943: "(Ljava/lang/String;)V");
1944: cv.visitInsn(ATHROW);
1945: cv.visitLabel(afterSwitch);
1946: }
1947:
1948: private List sortConstructors(ConstructorCallExpression call,
1949: ClassNode callNode) {
1950: // sort in a new list to prevent side effects
1951: List constructors = new ArrayList(callNode
1952: .getDeclaredConstructors());
1953: Comparator comp = new Comparator() {
1954: public int compare(Object arg0, Object arg1) {
1955: ConstructorNode c0 = (ConstructorNode) arg0;
1956: ConstructorNode c1 = (ConstructorNode) arg1;
1957: String descriptor0 = helper.getMethodDescriptor(
1958: ClassHelper.VOID_TYPE, c0.getParameters());
1959: String descriptor1 = helper.getMethodDescriptor(
1960: ClassHelper.VOID_TYPE, c1.getParameters());
1961: return descriptor0.compareTo(descriptor1);
1962: }
1963: };
1964: Collections.sort(constructors, comp);
1965: return constructors;
1966: }
1967:
1968: public void visitConstructorCallExpression(
1969: ConstructorCallExpression call) {
1970: onLineNumber(call, "visitConstructorCallExpression: \""
1971: + call.getType().getName() + "\":");
1972:
1973: if (call.isSpecialCall()) {
1974: visitSpecialConstructorCall(call);
1975: return;
1976: }
1977:
1978: Expression arguments = call.getArguments();
1979: if (arguments instanceof TupleExpression) {
1980: TupleExpression tupleExpression = (TupleExpression) arguments;
1981: int size = tupleExpression.getExpressions().size();
1982: if (size == 0) {
1983: arguments = MethodCallExpression.NO_ARGUMENTS;
1984: }
1985: }
1986:
1987: Expression receiverClass = new ClassExpression(call.getType());
1988: makeCall(receiverClass, null, arguments, invokeNew, false,
1989: false, false);
1990: }
1991:
1992: private static String makeFieldClassName(ClassNode type) {
1993: String internalName = BytecodeHelper.getClassInternalName(type);
1994: StringBuffer ret = new StringBuffer(internalName.length());
1995: for (int i = 0; i < internalName.length(); i++) {
1996: char c = internalName.charAt(i);
1997: if (c == '/') {
1998: ret.append('$');
1999: } else if (c == ';') {
2000: //append nothing -> delete ';'
2001: } else {
2002: ret.append(c);
2003: }
2004: }
2005: return ret.toString();
2006: }
2007:
2008: private static String getStaticFieldName(ClassNode type) {
2009: ClassNode componentType = type;
2010: String prefix = "";
2011: for (; componentType.isArray(); componentType = componentType
2012: .getComponentType()) {
2013: prefix += "$";
2014: }
2015: if (prefix.length() != 0)
2016: prefix = "array" + prefix;
2017: String name = prefix + "class$"
2018: + makeFieldClassName(componentType);
2019: return name;
2020: }
2021:
2022: private void visitAttributeOrProperty(
2023: PropertyExpression expression,
2024: MethodCallerMultiAdapter adapter) {
2025: Expression objectExpression = expression.getObjectExpression();
2026: if (isThisExpression(objectExpression)) {
2027: // lets use the field expression if its available
2028: String name = expression.getPropertyAsString();
2029: if (name != null) {
2030: FieldNode field = classNode.getField(name);
2031: if (field != null) {
2032: visitFieldExpression(new FieldExpression(field));
2033: return;
2034: }
2035: }
2036: }
2037:
2038: // arguments already on stack if any
2039: makeCall(
2040: objectExpression, // receiver
2041: new CastExpression(ClassHelper.STRING_TYPE, expression
2042: .getProperty()), // messageName
2043: MethodCallExpression.NO_ARGUMENTS, adapter, expression
2044: .isSafe(), expression.isSpreadSafe(),
2045: expression.isImplicitThis());
2046: }
2047:
2048: private boolean isStaticContext() {
2049: if (!isInClosure())
2050: return false;
2051: if (constructorNode != null)
2052: return false;
2053: return classNode.isStaticClass() || methodNode.isStatic();
2054: }
2055:
2056: public void visitPropertyExpression(PropertyExpression expression) {
2057: Expression objectExpression = expression.getObjectExpression();
2058: MethodCallerMultiAdapter adapter;
2059: if (leftHandExpression) {
2060: adapter = setProperty;
2061: if (isGroovyObject(objectExpression))
2062: adapter = setGroovyObjectProperty;
2063: if (isStaticContext() && isThisOrSuper(objectExpression))
2064: adapter = setProperty;
2065: } else {
2066: adapter = getProperty;
2067: if (isGroovyObject(objectExpression))
2068: adapter = getGroovyObjectProperty;
2069: if (isStaticContext() && isThisOrSuper(objectExpression))
2070: adapter = getProperty;
2071: }
2072: visitAttributeOrProperty(expression, adapter);
2073: }
2074:
2075: public void visitAttributeExpression(AttributeExpression expression) {
2076: Expression objectExpression = expression.getObjectExpression();
2077: MethodCallerMultiAdapter adapter;
2078: if (leftHandExpression) {
2079: adapter = setField;
2080: if (isGroovyObject(objectExpression))
2081: adapter = setGroovyObjectField;
2082: if (usesSuper(expression))
2083: adapter = getFieldOnSuper;
2084: } else {
2085: adapter = getField;
2086: if (isGroovyObject(objectExpression))
2087: adapter = getGroovyObjectField;
2088: if (usesSuper(expression))
2089: adapter = getFieldOnSuper;
2090: }
2091: visitAttributeOrProperty(expression, adapter);
2092: }
2093:
2094: protected boolean isGroovyObject(Expression objectExpression) {
2095: return isThisExpression(objectExpression);
2096: }
2097:
2098: public void visitFieldExpression(FieldExpression expression) {
2099: FieldNode field = expression.getField();
2100:
2101: if (field.isStatic()) {
2102: if (leftHandExpression) {
2103: storeStaticField(expression);
2104: } else {
2105: loadStaticField(expression);
2106: }
2107: } else {
2108: if (leftHandExpression) {
2109: storeThisInstanceField(expression);
2110: } else {
2111: loadInstanceField(expression);
2112: }
2113: }
2114: }
2115:
2116: /**
2117: *
2118: * @param fldExp
2119: */
2120: public void loadStaticField(FieldExpression fldExp) {
2121: FieldNode field = fldExp.getField();
2122: boolean holder = field.isHolder() && !isInClosureConstructor();
2123: ClassNode type = field.getType();
2124:
2125: String ownerName = (field.getOwner().equals(classNode)) ? internalClassName
2126: : BytecodeHelper.getClassInternalName(field.getOwner());
2127: if (holder) {
2128: cv.visitFieldInsn(GETSTATIC, ownerName, fldExp
2129: .getFieldName(), BytecodeHelper
2130: .getTypeDescription(type));
2131: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference",
2132: "get", "()Ljava/lang/Object;");
2133: } else {
2134: cv.visitFieldInsn(GETSTATIC, ownerName, fldExp
2135: .getFieldName(), BytecodeHelper
2136: .getTypeDescription(type));
2137: if (ClassHelper.isPrimitiveType(type)) {
2138: helper.box(type);
2139: } else {
2140: }
2141: }
2142: }
2143:
2144: /**
2145: * RHS instance field. should move most of the code in the BytecodeHelper
2146: * @param fldExp
2147: */
2148: public void loadInstanceField(FieldExpression fldExp) {
2149: FieldNode field = fldExp.getField();
2150: boolean holder = field.isHolder() && !isInClosureConstructor();
2151: ClassNode type = field.getType();
2152: String ownerName = (field.getOwner().equals(classNode)) ? internalClassName
2153: : helper.getClassInternalName(field.getOwner());
2154:
2155: cv.visitVarInsn(ALOAD, 0);
2156: cv.visitFieldInsn(GETFIELD, ownerName, fldExp.getFieldName(),
2157: BytecodeHelper.getTypeDescription(type));
2158:
2159: if (holder) {
2160: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference",
2161: "get", "()Ljava/lang/Object;");
2162: } else {
2163: if (ClassHelper.isPrimitiveType(type)) {
2164: helper.box(type);
2165: } else {
2166: }
2167: }
2168: }
2169:
2170: public void storeThisInstanceField(FieldExpression expression) {
2171: FieldNode field = expression.getField();
2172:
2173: boolean holder = field.isHolder() && !isInClosureConstructor();
2174: ClassNode type = field.getType();
2175:
2176: String ownerName = (field.getOwner().equals(classNode)) ? internalClassName
2177: : BytecodeHelper.getClassInternalName(field.getOwner());
2178: if (holder) {
2179: cv.visitVarInsn(ALOAD, 0);
2180: cv.visitFieldInsn(GETFIELD, ownerName, expression
2181: .getFieldName(), BytecodeHelper
2182: .getTypeDescription(type));
2183: cv.visitInsn(SWAP);
2184: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference",
2185: "set", "(Ljava/lang/Object;)V");
2186: } else {
2187: if (isInClosureConstructor()) {
2188: helper.doCast(type);
2189: } else if (!ClassHelper.isPrimitiveType(type)) {
2190: doConvertAndCast(type);
2191: }
2192: cv.visitVarInsn(ALOAD, 0);
2193: //helper.swapObjectWith(type);
2194: cv.visitInsn(SWAP);
2195: helper.unbox(type);
2196: helper.putField(field, ownerName);
2197: }
2198: }
2199:
2200: public void storeStaticField(FieldExpression expression) {
2201: FieldNode field = expression.getField();
2202:
2203: boolean holder = field.isHolder() && !isInClosureConstructor();
2204:
2205: ClassNode type = field.getType();
2206:
2207: String ownerName = (field.getOwner().equals(classNode)) ? internalClassName
2208: : helper.getClassInternalName(field.getOwner());
2209: if (holder) {
2210: cv.visitFieldInsn(GETSTATIC, ownerName, expression
2211: .getFieldName(), BytecodeHelper
2212: .getTypeDescription(type));
2213: cv.visitInsn(SWAP);
2214: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Reference",
2215: "set", "(Ljava/lang/Object;)V");
2216: } else {
2217: helper.doCast(type);
2218: cv.visitFieldInsn(PUTSTATIC, ownerName, expression
2219: .getFieldName(), BytecodeHelper
2220: .getTypeDescription(type));
2221: }
2222: }
2223:
2224: protected void visitOuterFieldExpression(
2225: FieldExpression expression, ClassNode outerClassNode,
2226: int steps, boolean first) {
2227: FieldNode field = expression.getField();
2228: boolean isStatic = field.isStatic();
2229:
2230: int tempIdx = compileStack.defineTemporaryVariable(field,
2231: leftHandExpression && first);
2232:
2233: if (steps > 1 || !isStatic) {
2234: cv.visitVarInsn(ALOAD, 0);
2235: cv.visitFieldInsn(GETFIELD, internalClassName, "owner",
2236: BytecodeHelper.getTypeDescription(outerClassNode));
2237: }
2238:
2239: if (steps == 1) {
2240: int opcode = (leftHandExpression) ? ((isStatic) ? PUTSTATIC
2241: : PUTFIELD) : ((isStatic) ? GETSTATIC : GETFIELD);
2242: String ownerName = BytecodeHelper
2243: .getClassInternalName(outerClassNode);
2244:
2245: if (leftHandExpression) {
2246: cv.visitVarInsn(ALOAD, tempIdx);
2247: boolean holder = field.isHolder()
2248: && !isInClosureConstructor();
2249: if (!holder) {
2250: doConvertAndCast(field.getType());
2251: }
2252: }
2253: cv.visitFieldInsn(opcode, ownerName, expression
2254: .getFieldName(), BytecodeHelper
2255: .getTypeDescription(field.getType()));
2256: if (!leftHandExpression) {
2257: if (ClassHelper.isPrimitiveType(field.getType())) {
2258: helper.box(field.getType());
2259: }
2260: }
2261: }
2262:
2263: else {
2264: visitOuterFieldExpression(expression, outerClassNode
2265: .getOuterClass(), steps - 1, false);
2266: }
2267: }
2268:
2269: /**
2270: * Visits a bare (unqualified) variable expression.
2271: */
2272:
2273: public void visitVariableExpression(VariableExpression expression) {
2274:
2275: String variableName = expression.getName();
2276:
2277: //-----------------------------------------------------------------------
2278: // SPECIAL CASES
2279:
2280: //
2281: // "this" for static methods is the Class instance
2282:
2283: ClassNode classNode = this .classNode;
2284: if (isInClosure())
2285: classNode = getOutermostClass();
2286:
2287: if (variableName.equals("this")) {
2288: if (isStaticMethod()
2289: || (!implicitThis && isStaticContext())) {
2290: visitClassExpression(new ClassExpression(classNode));
2291: } else {
2292: loadThis();
2293: }
2294: return;
2295: }
2296:
2297: //
2298: // "super" also requires special handling
2299:
2300: if (variableName.equals("super")) {
2301: if (isStaticMethod()) {
2302: visitClassExpression(new ClassExpression(classNode
2303: .getSuperClass()));
2304: } else {
2305: loadThis();
2306: }
2307: return; // <<< FLOW CONTROL <<<<<<<<<
2308: }
2309:
2310: Variable variable = compileStack.getVariable(variableName,
2311: false);
2312:
2313: VariableScope scope = compileStack.getScope();
2314: if (variable == null) {
2315: processClassVariable(variableName);
2316: } else {
2317: processStackVariable(variable);
2318: }
2319: }
2320:
2321: private void loadThis() {
2322: cv.visitVarInsn(ALOAD, 0);
2323: if (!implicitThis && isInClosure()) {
2324: cv.visitMethodInsn(INVOKEVIRTUAL, "groovy/lang/Closure",
2325: "getThisObject", "()Ljava/lang/Object;");
2326: }
2327: }
2328:
2329: protected void processStackVariable(Variable variable) {
2330: if (leftHandExpression) {
2331: helper.storeVar(variable);
2332: } else {
2333: helper.loadVar(variable);
2334: }
2335: if (ASM_DEBUG) {
2336: helper.mark("var: " + variable.getName());
2337: }
2338: }
2339:
2340: protected void processClassVariable(String name) {
2341: if (passingClosureParams && isInScriptBody()) {
2342: // lets create a ScriptReference to pass into the closure
2343: cv.visitTypeInsn(NEW,
2344: "org/codehaus/groovy/runtime/ScriptReference");
2345: cv.visitInsn(DUP);
2346:
2347: loadThisOrOwner();
2348: cv.visitLdcInsn(name);
2349:
2350: cv.visitMethodInsn(INVOKESPECIAL,
2351: "org/codehaus/groovy/runtime/ScriptReference",
2352: "<init>",
2353: "(Lgroovy/lang/Script;Ljava/lang/String;)V");
2354: } else {
2355: PropertyExpression pexp = new PropertyExpression(
2356: VariableExpression.THIS_EXPRESSION, name);
2357: pexp.setImplicitThis(true);
2358: visitPropertyExpression(pexp);
2359: }
2360: }
2361:
2362: protected void processFieldAccess(String name, FieldNode field,
2363: int steps) {
2364: FieldExpression expression = new FieldExpression(field);
2365:
2366: if (steps == 0) {
2367: visitFieldExpression(expression);
2368: } else {
2369: visitOuterFieldExpression(expression, classNode
2370: .getOuterClass(), steps, true);
2371: }
2372: }
2373:
2374: /**
2375: * @return true if we are in a script body, where all variables declared are no longer
2376: * local variables but are properties
2377: */
2378: protected boolean isInScriptBody() {
2379: if (classNode.isScriptBody()) {
2380: return true;
2381: } else {
2382: return classNode.isScript() && methodNode != null
2383: && methodNode.getName().equals("run");
2384: }
2385: }
2386:
2387: /**
2388: * @return true if this expression will have left a value on the stack
2389: * that must be popped
2390: */
2391: protected boolean isPopRequired(Expression expression) {
2392: if (expression instanceof MethodCallExpression) {
2393: if (expression.getType() == ClassHelper.VOID_TYPE) { // nothing on the stack
2394: return false;
2395: } else {
2396: return !usesSuper((MethodCallExpression) expression);
2397: }
2398: }
2399: if (expression instanceof DeclarationExpression) {
2400: return false;
2401: }
2402: if (expression instanceof BinaryExpression) {
2403: BinaryExpression binExp = (BinaryExpression) expression;
2404: switch (binExp.getOperation().getType()) { // br todo should leave a copy of the value on the stack for all the assignemnt.
2405: // case Types.EQUAL : // br a copy of the right value is left on the stack (see evaluateEqual()) so a pop is required for a standalone assignment
2406: // case Types.PLUS_EQUAL : // this and the following are related to evaluateBinaryExpressionWithAsignment()
2407: // case Types.MINUS_EQUAL :
2408: // case Types.MULTIPLY_EQUAL :
2409: // case Types.DIVIDE_EQUAL :
2410: // case Types.INTDIV_EQUAL :
2411: // case Types.MOD_EQUAL :
2412: // return false;
2413: }
2414: }
2415: if (expression instanceof ConstructorCallExpression) {
2416: ConstructorCallExpression cce = (ConstructorCallExpression) expression;
2417: return !cce.isSpecialCall();
2418: }
2419: return true;
2420: }
2421:
2422: protected void createInterfaceSyntheticStaticFields() {
2423: if (syntheticStaticFields.isEmpty())
2424: return;
2425:
2426: addInnerClass(interfaceClassLoadingClass);
2427:
2428: for (Iterator iter = syntheticStaticFields.iterator(); iter
2429: .hasNext();) {
2430: String staticFieldName = (String) iter.next();
2431: // generate a field node
2432: interfaceClassLoadingClass.addField(staticFieldName,
2433: ACC_STATIC + ACC_SYNTHETIC, ClassHelper.CLASS_Type,
2434: null);
2435: }
2436: }
2437:
2438: protected void createSyntheticStaticFields() {
2439: for (Iterator iter = syntheticStaticFields.iterator(); iter
2440: .hasNext();) {
2441: String staticFieldName = (String) iter.next();
2442: // generate a field node
2443: FieldNode fn = classNode.getField(staticFieldName);
2444: if (fn != null) {
2445: boolean type = fn.getType() == ClassHelper.CLASS_Type;
2446: boolean modifiers = fn.getModifiers() == ACC_STATIC
2447: + ACC_SYNTHETIC;
2448: if (type && modifiers)
2449: continue;
2450: String text = "";
2451: if (!type)
2452: text = " with wrong type: " + fn.getType()
2453: + " (java.lang.Class needed)";
2454: if (!modifiers)
2455: text = " with wrong modifiers: "
2456: + fn.getModifiers() + " ("
2457: + (ACC_STATIC + ACC_SYNTHETIC) + " needed)";
2458: throwException("tried to set a static syntethic field "
2459: + staticFieldName
2460: + " in "
2461: + classNode.getName()
2462: + " for class resolving, but found alreeady a node of that"
2463: + " name " + text);
2464: } else {
2465: cw.visitField(ACC_STATIC + ACC_SYNTHETIC,
2466: staticFieldName, "Ljava/lang/Class;", null,
2467: null);
2468: }
2469: }
2470:
2471: cv = cw.visitMethod(ACC_STATIC + ACC_SYNTHETIC, "class$",
2472: "(Ljava/lang/String;)Ljava/lang/Class;", null, null);
2473: Label l0 = new Label();
2474: cv.visitLabel(l0);
2475: cv.visitVarInsn(ALOAD, 0);
2476: cv.visitMethodInsn(INVOKESTATIC, "java/lang/Class", "forName",
2477: "(Ljava/lang/String;)Ljava/lang/Class;");
2478: Label l1 = new Label();
2479: cv.visitLabel(l1);
2480: cv.visitInsn(ARETURN);
2481: Label l2 = new Label();
2482: cv.visitLabel(l2);
2483: cv.visitVarInsn(ASTORE, 1);
2484: cv.visitTypeInsn(NEW, "java/lang/NoClassDefFoundError");
2485: cv.visitInsn(DUP);
2486: cv.visitVarInsn(ALOAD, 1);
2487: cv.visitMethodInsn(INVOKEVIRTUAL,
2488: "java/lang/ClassNotFoundException", "getMessage",
2489: "()Ljava/lang/String;");
2490: cv.visitMethodInsn(INVOKESPECIAL,
2491: "java/lang/NoClassDefFoundError", "<init>",
2492: "(Ljava/lang/String;)V");
2493: cv.visitInsn(ATHROW);
2494: cv.visitTryCatchBlock(l0, l2, l2,
2495: "java/lang/ClassNotFoundException"); // br using l2 as the 2nd param seems create the right table entry
2496: cv.visitMaxs(3, 2);
2497: }
2498:
2499: /** load class object on stack */
2500: public void visitClassExpression(ClassExpression expression) {
2501: ClassNode type = expression.getType();
2502:
2503: if (ClassHelper.isPrimitiveType(type)) {
2504: ClassNode objectType = ClassHelper.getWrapper(type);
2505: cv.visitFieldInsn(GETSTATIC, BytecodeHelper
2506: .getClassInternalName(objectType), "TYPE",
2507: "Ljava/lang/Class;");
2508: } else {
2509: String staticFieldName;
2510: if (type.equals(classNode)) {
2511: staticFieldName = "class$0";
2512: if (compileStack.getCurrentClassIndex() != -1) {
2513: cv.visitVarInsn(ALOAD, compileStack
2514: .getCurrentClassIndex());
2515: return;
2516: }
2517: } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
2518: staticFieldName = getStaticFieldName(type);
2519: if (compileStack.getCurrentMetaClassIndex() != -1) {
2520: cv.visitVarInsn(ALOAD, compileStack
2521: .getCurrentMetaClassIndex());
2522: return;
2523: }
2524: } else {
2525: staticFieldName = getStaticFieldName(type);
2526: }
2527:
2528: syntheticStaticFields.add(staticFieldName);
2529:
2530: String internalClassName = this .internalClassName;
2531: if (classNode.isInterface()) {
2532: internalClassName = BytecodeHelper
2533: .getClassInternalName(interfaceClassLoadingClass);
2534: }
2535:
2536: cv.visitFieldInsn(GETSTATIC, internalClassName,
2537: staticFieldName, "Ljava/lang/Class;");
2538:
2539: Label l0 = new Label();
2540: cv.visitJumpInsn(IFNONNULL, l0);
2541: cv.visitLdcInsn(BytecodeHelper
2542: .getClassLoadingTypeDescription(type));
2543: cv.visitMethodInsn(INVOKESTATIC, internalClassName,
2544: "class$", "(Ljava/lang/String;)Ljava/lang/Class;");
2545: cv.visitInsn(DUP);
2546: cv.visitFieldInsn(PUTSTATIC, internalClassName,
2547: staticFieldName, "Ljava/lang/Class;");
2548: Label l1 = new Label();
2549: cv.visitJumpInsn(GOTO, l1);
2550: cv.visitLabel(l0);
2551: cv.visitFieldInsn(GETSTATIC, internalClassName,
2552: staticFieldName, "Ljava/lang/Class;");
2553: cv.visitLabel(l1);
2554:
2555: if (type.equals(classNode)) {
2556: cv.visitInsn(DUP);
2557: int index = compileStack.defineTemporaryVariable(
2558: "class$0", ClassHelper.CLASS_Type, true);
2559: compileStack.setCurrentClassIndex(index);
2560: } else if (type.equals(ClassHelper.METACLASS_TYPE)) {
2561: cv.visitInsn(DUP);
2562: int index = compileStack.defineTemporaryVariable(
2563: "meta$class$0", ClassHelper.CLASS_Type, true);
2564: compileStack.setCurrentMetaClassIndex(index);
2565: }
2566: }
2567: }
2568:
2569: public void visitRangeExpression(RangeExpression expression) {
2570: expression.getFrom().visit(this );
2571: expression.getTo().visit(this );
2572:
2573: helper.pushConstant(expression.isInclusive());
2574:
2575: createRangeMethod.call(cv);
2576: }
2577:
2578: public void visitMapEntryExpression(MapEntryExpression expression) {
2579: throw new GroovyBugError(
2580: "MapEntryExpression should not be visited here");
2581: }
2582:
2583: public void visitMapExpression(MapExpression expression) {
2584: List entries = expression.getMapEntryExpressions();
2585: int size = entries.size();
2586: helper.pushConstant(size * 2);
2587:
2588: cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2589:
2590: int i = 0;
2591: for (Iterator iter = entries.iterator(); iter.hasNext();) {
2592: Object object = iter.next();
2593: MapEntryExpression entry = (MapEntryExpression) object;
2594:
2595: cv.visitInsn(DUP);
2596: helper.pushConstant(i++);
2597: visitAndAutoboxBoolean(entry.getKeyExpression());
2598: cv.visitInsn(AASTORE);
2599:
2600: cv.visitInsn(DUP);
2601: helper.pushConstant(i++);
2602: visitAndAutoboxBoolean(entry.getValueExpression());
2603: cv.visitInsn(AASTORE);
2604: }
2605: createMapMethod.call(cv);
2606: }
2607:
2608: public void visitArgumentlistExpression(ArgumentListExpression ale) {
2609: if (containsSpreadExpression(ale)) {
2610: despreadList(ale.getExpressions(), true);
2611: } else {
2612: visitTupleExpression(ale, true);
2613: }
2614: }
2615:
2616: public void visitTupleExpression(TupleExpression expression) {
2617: visitTupleExpression(expression, false);
2618: }
2619:
2620: private void visitTupleExpression(TupleExpression expression,
2621: boolean useWrapper) {
2622: int size = expression.getExpressions().size();
2623:
2624: helper.pushConstant(size);
2625:
2626: cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2627:
2628: for (int i = 0; i < size; i++) {
2629: cv.visitInsn(DUP);
2630: helper.pushConstant(i);
2631: Expression argument = expression.getExpression(i);
2632: visitAndAutoboxBoolean(argument);
2633: if (useWrapper && argument instanceof CastExpression)
2634: loadWrapper(argument);
2635:
2636: cv.visitInsn(AASTORE);
2637: }
2638: }
2639:
2640: private void loadWrapper(Expression argument) {
2641: ClassNode goalClass = argument.getType();
2642: visitClassExpression(new ClassExpression(goalClass));
2643: if (goalClass.isDerivedFromGroovyObject()) {
2644: createGroovyObjectWrapperMethod.call(cv);
2645: } else {
2646: createPojoWrapperMethod.call(cv);
2647: }
2648: }
2649:
2650: public void visitArrayExpression(ArrayExpression expression) {
2651: ClassNode elementType = expression.getElementType();
2652: String arrayTypeName = BytecodeHelper
2653: .getClassInternalName(elementType);
2654: List sizeExpression = expression.getSizeExpression();
2655:
2656: int size = 0;
2657: int dimensions = 0;
2658: if (sizeExpression != null) {
2659: for (Iterator iter = sizeExpression.iterator(); iter
2660: .hasNext();) {
2661: Expression element = (Expression) iter.next();
2662: if (element == ConstantExpression.EMTPY_EXPRESSION)
2663: break;
2664: dimensions++;
2665: // lets convert to an int
2666: visitAndAutoboxBoolean(element);
2667: helper.unbox(int.class);
2668: }
2669: } else {
2670: size = expression.getExpressions().size();
2671: helper.pushConstant(size);
2672: }
2673:
2674: int storeIns = AASTORE;
2675: if (sizeExpression != null) {
2676: arrayTypeName = BytecodeHelper
2677: .getTypeDescription(expression.getType());
2678: cv.visitMultiANewArrayInsn(arrayTypeName, dimensions);
2679: } else if (ClassHelper.isPrimitiveType(elementType)) {
2680: int primType = 0;
2681: if (elementType == ClassHelper.boolean_TYPE) {
2682: primType = T_BOOLEAN;
2683: storeIns = BASTORE;
2684: } else if (elementType == ClassHelper.char_TYPE) {
2685: primType = T_CHAR;
2686: storeIns = CASTORE;
2687: } else if (elementType == ClassHelper.float_TYPE) {
2688: primType = T_FLOAT;
2689: storeIns = FASTORE;
2690: } else if (elementType == ClassHelper.double_TYPE) {
2691: primType = T_DOUBLE;
2692: storeIns = DASTORE;
2693: } else if (elementType == ClassHelper.byte_TYPE) {
2694: primType = T_BYTE;
2695: storeIns = BASTORE;
2696: } else if (elementType == ClassHelper.short_TYPE) {
2697: primType = T_SHORT;
2698: storeIns = SASTORE;
2699: } else if (elementType == ClassHelper.int_TYPE) {
2700: primType = T_INT;
2701: storeIns = IASTORE;
2702: } else if (elementType == ClassHelper.long_TYPE) {
2703: primType = T_LONG;
2704: storeIns = LASTORE;
2705: }
2706: cv.visitIntInsn(NEWARRAY, primType);
2707: } else {
2708: cv.visitTypeInsn(ANEWARRAY, arrayTypeName);
2709: }
2710:
2711: for (int i = 0; i < size; i++) {
2712: cv.visitInsn(DUP);
2713: helper.pushConstant(i);
2714: Expression elementExpression = expression.getExpression(i);
2715: if (elementExpression == null) {
2716: ConstantExpression.NULL.visit(this );
2717: } else {
2718: if (!elementType.equals(elementExpression.getType())) {
2719: visitCastExpression(new CastExpression(elementType,
2720: elementExpression, true));
2721: } else {
2722: visitAndAutoboxBoolean(elementExpression);
2723: }
2724: }
2725: cv.visitInsn(storeIns);
2726: }
2727:
2728: if (sizeExpression == null
2729: && ClassHelper.isPrimitiveType(elementType)) {
2730: int par = compileStack.defineTemporaryVariable("par", true);
2731: cv.visitVarInsn(ALOAD, par);
2732: }
2733: }
2734:
2735: public void visitListExpression(ListExpression expression) {
2736: int size = expression.getExpressions().size();
2737: boolean containsSpreadExpression = containsSpreadExpression(expression);
2738: if (!containsSpreadExpression) {
2739: helper.pushConstant(size);
2740:
2741: cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2742:
2743: for (int i = 0; i < size; i++) {
2744: cv.visitInsn(DUP);
2745: helper.pushConstant(i);
2746: visitAndAutoboxBoolean(expression.getExpression(i));
2747: cv.visitInsn(AASTORE);
2748: }
2749: } else {
2750: despreadList(expression.getExpressions(), false);
2751: }
2752: createListMethod.call(cv);
2753: }
2754:
2755: public void visitGStringExpression(GStringExpression expression) {
2756: int size = expression.getValues().size();
2757: helper.pushConstant(size);
2758:
2759: cv.visitTypeInsn(ANEWARRAY, "java/lang/Object");
2760:
2761: for (int i = 0; i < size; i++) {
2762: cv.visitInsn(DUP);
2763: helper.pushConstant(i);
2764: visitAndAutoboxBoolean(expression.getValue(i));
2765: cv.visitInsn(AASTORE);
2766: }
2767:
2768: int paramIdx = compileStack.defineTemporaryVariable("iterator",
2769: true);
2770:
2771: ClassNode innerClass = createGStringClass(expression);
2772: addInnerClass(innerClass);
2773: String innerClassinternalName = BytecodeHelper
2774: .getClassInternalName(innerClass);
2775:
2776: cv.visitTypeInsn(NEW, innerClassinternalName);
2777: cv.visitInsn(DUP);
2778: cv.visitVarInsn(ALOAD, paramIdx);
2779:
2780: cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName,
2781: "<init>", "([Ljava/lang/Object;)V");
2782: compileStack.removeVar(paramIdx);
2783: }
2784:
2785: public void visitAnnotations(AnnotatedNode node) {
2786: Map annotionMap = node.getAnnotations();
2787: if (annotionMap.isEmpty())
2788: return;
2789: Iterator it = annotionMap.values().iterator();
2790: while (it.hasNext()) {
2791: AnnotationNode an = (AnnotationNode) it.next();
2792: //skip builtin properties
2793: if (an.isBuiltIn())
2794: continue;
2795: ClassNode type = an.getClassNode();
2796:
2797: String clazz = type.getName();
2798: AnnotationVisitor av = cw.visitAnnotation(BytecodeHelper
2799: .formatNameForClassLoading(clazz), false);
2800:
2801: Iterator mIt = an.getMembers().keySet().iterator();
2802: while (mIt.hasNext()) {
2803: String name = (String) mIt.next();
2804: ConstantExpression exp = (ConstantExpression) an
2805: .getMember(name);
2806: av.visit(name, exp.getValue());
2807: }
2808: av.visitEnd();
2809: }
2810: }
2811:
2812: // Implementation methods
2813: //-------------------------------------------------------------------------
2814: protected boolean addInnerClass(ClassNode innerClass) {
2815: innerClass.setModule(classNode.getModule());
2816: return innerClasses.add(innerClass);
2817: }
2818:
2819: protected ClassNode createClosureClass(ClosureExpression expression) {
2820: ClassNode outerClass = getOutermostClass();
2821: String name = outerClass.getName()
2822: + "$"
2823: + context.getNextClosureInnerName(outerClass,
2824: classNode, methodNode); // br added a more infomative name
2825: boolean staticMethodOrInStaticClass = isStaticMethod()
2826: || classNode.isStaticClass();
2827:
2828: Parameter[] parameters = expression.getParameters();
2829: if (parameters == null) {
2830: parameters = new Parameter[0];
2831: } else if (parameters.length == 0) {
2832: // lets create a default 'it' parameter
2833: parameters = new Parameter[] { new Parameter(
2834: ClassHelper.OBJECT_TYPE, "it",
2835: ConstantExpression.NULL) };
2836: }
2837:
2838: Parameter[] localVariableParams = getClosureSharedVariables(expression);
2839:
2840: InnerClassNode answer = new InnerClassNode(outerClass, name, 0,
2841: ClassHelper.CLOSURE_TYPE); // closures are local inners and not public
2842: answer.setEnclosingMethod(this .methodNode);
2843: answer.setSynthetic(true);
2844:
2845: if (staticMethodOrInStaticClass) {
2846: answer.setStaticClass(true);
2847: }
2848: if (isInScriptBody()) {
2849: answer.setScriptBody(true);
2850: }
2851: MethodNode method = answer.addMethod("doCall", ACC_PUBLIC,
2852: ClassHelper.OBJECT_TYPE, parameters,
2853: ClassNode.EMPTY_ARRAY, expression.getCode());
2854: method.setSourcePosition(expression);
2855:
2856: VariableScope varScope = expression.getVariableScope();
2857: if (varScope == null) {
2858: throw new RuntimeException(
2859: "Must have a VariableScope by now! for expression: "
2860: + expression + " class: " + name);
2861: } else {
2862: method.setVariableScope(varScope.copy());
2863: }
2864: if (parameters.length > 1
2865: || (parameters.length == 1
2866: && parameters[0].getType() != null && parameters[0]
2867: .getType() != ClassHelper.OBJECT_TYPE)) {
2868:
2869: // lets add a typesafe call method
2870: MethodNode call = answer.addMethod("call", ACC_PUBLIC,
2871: ClassHelper.OBJECT_TYPE, parameters,
2872: ClassNode.EMPTY_ARRAY, new ReturnStatement(
2873: new MethodCallExpression(
2874: VariableExpression.THIS_EXPRESSION,
2875: "doCall",
2876: new ArgumentListExpression(
2877: parameters))));
2878: call.setSourcePosition(expression);
2879: }
2880:
2881: // lets make the constructor
2882: BlockStatement block = new BlockStatement();
2883: block.setSourcePosition(expression);
2884: VariableExpression outer = new VariableExpression(
2885: "_outerInstance");
2886: outer.setSourcePosition(expression);
2887: block.getVariableScope().getReferencedLocalVariables().put(
2888: "_outerInstance", outer);
2889: VariableExpression this Object = new VariableExpression(
2890: "_thisObject");
2891: this Object.setSourcePosition(expression);
2892: block.getVariableScope().getReferencedLocalVariables().put(
2893: "_thisObject", this Object);
2894: TupleExpression conArgs = new TupleExpression();
2895: conArgs.addExpression(outer);
2896: conArgs.addExpression(this Object);
2897: block
2898: .addStatement(new ExpressionStatement(
2899: new ConstructorCallExpression(ClassNode.SUPER,
2900: conArgs)));
2901:
2902: // lets assign all the parameter fields from the outer context
2903: for (int i = 0; i < localVariableParams.length; i++) {
2904: Parameter param = localVariableParams[i];
2905: String paramName = param.getName();
2906: Expression initialValue = null;
2907: ClassNode type = param.getType();
2908: FieldNode paramField = null;
2909: if (true) {
2910: initialValue = new VariableExpression(paramName);
2911: ClassNode realType = type;
2912: type = ClassHelper.makeReference();
2913: param.setType(type);
2914: paramField = answer.addField(paramName, ACC_PRIVATE,
2915: type, initialValue);
2916: paramField.setHolder(true);
2917: String methodName = Verifier.capitalize(paramName);
2918:
2919: // lets add a getter & setter
2920: Expression fieldExp = new FieldExpression(paramField);
2921: answer.addMethod("get" + methodName, ACC_PUBLIC,
2922: realType, Parameter.EMPTY_ARRAY,
2923: ClassNode.EMPTY_ARRAY, new ReturnStatement(
2924: fieldExp));
2925:
2926: /*
2927: answer.addMethod(
2928: "set" + methodName,
2929: ACC_PUBLIC,
2930: "void",
2931: new Parameter[] { new Parameter(realType, "__value") },
2932: new ExpressionStatement(
2933: new BinaryExpression(expression, Token.newSymbol(Types.EQUAL, 0, 0), new VariableExpression("__value"))));
2934: */
2935: }
2936: }
2937:
2938: Parameter[] params = new Parameter[2 + localVariableParams.length];
2939: params[0] = new Parameter(ClassHelper.OBJECT_TYPE,
2940: "_outerInstance");
2941: params[1] = new Parameter(ClassHelper.OBJECT_TYPE,
2942: "_thisObject");
2943: System.arraycopy(localVariableParams, 0, params, 2,
2944: localVariableParams.length);
2945:
2946: ASTNode sn = answer.addConstructor(ACC_PUBLIC, params,
2947: ClassNode.EMPTY_ARRAY, block);
2948: sn.setSourcePosition(expression);
2949: return answer;
2950: }
2951:
2952: protected Parameter[] getClosureSharedVariables(ClosureExpression ce) {
2953: VariableScope scope = ce.getVariableScope();
2954: Map references = scope.getReferencedLocalVariables();
2955: Parameter[] ret = new Parameter[references.size()];
2956: int index = 0;
2957: for (Iterator iter = references.values().iterator(); iter
2958: .hasNext();) {
2959: org.codehaus.groovy.ast.Variable element = (org.codehaus.groovy.ast.Variable) iter
2960: .next();
2961: if (element instanceof Parameter) {
2962: ret[index] = (Parameter) element;
2963: } else {
2964: Parameter p = new Parameter(element.getType(), element
2965: .getName());
2966: ret[index] = p;
2967: }
2968: index++;
2969: }
2970: return ret;
2971: }
2972:
2973: protected ClassNode getOutermostClass() {
2974: if (outermostClass == null) {
2975: outermostClass = classNode;
2976: while (outermostClass instanceof InnerClassNode) {
2977: outermostClass = outermostClass.getOuterClass();
2978: }
2979: }
2980: return outermostClass;
2981: }
2982:
2983: protected ClassNode createGStringClass(GStringExpression expression) {
2984: ClassNode owner = classNode;
2985: if (owner instanceof InnerClassNode) {
2986: owner = owner.getOuterClass();
2987: }
2988: String outerClassName = owner.getName();
2989: String name = outerClassName + "$"
2990: + context.getNextInnerClassIdx();
2991: InnerClassNode answer = new InnerClassNode(owner, name, 0,
2992: ClassHelper.GSTRING_TYPE);
2993: answer.setEnclosingMethod(this .methodNode);
2994: FieldNode stringsField = answer.addField("strings",
2995: ACC_PRIVATE /*| ACC_STATIC*/, ClassHelper.STRING_TYPE
2996: .makeArray(), new ArrayExpression(
2997: ClassHelper.STRING_TYPE, expression
2998: .getStrings()));
2999: answer.addMethod("getStrings", ACC_PUBLIC,
3000: ClassHelper.STRING_TYPE.makeArray(),
3001: Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY,
3002: new ReturnStatement(new FieldExpression(stringsField)));
3003: // lets make the constructor
3004: BlockStatement block = new BlockStatement();
3005: block.addStatement(new ExpressionStatement(
3006: new ConstructorCallExpression(ClassNode.SUPER,
3007: new VariableExpression("values"))));
3008: Parameter[] contructorParams = new Parameter[] { new Parameter(
3009: ClassHelper.OBJECT_TYPE.makeArray(), "values") };
3010: answer.addConstructor(ACC_PUBLIC, contructorParams,
3011: ClassNode.EMPTY_ARRAY, block);
3012: return answer;
3013: }
3014:
3015: protected void doConvertAndCast(ClassNode type) {
3016: doConvertAndCast(type, false);
3017: }
3018:
3019: protected void doConvertAndCast(ClassNode type, boolean coerce) {
3020: if (type == ClassHelper.OBJECT_TYPE)
3021: return;
3022: if (isValidTypeForCast(type)) {
3023: visitClassExpression(new ClassExpression(type));
3024: if (coerce) {
3025: asTypeMethod.call(cv);
3026: } else {
3027: castToTypeMethod.call(cv);
3028: }
3029: }
3030: helper.doCast(type);
3031: }
3032:
3033: protected void evaluateLogicalOrExpression(
3034: BinaryExpression expression) {
3035: visitBooleanExpression(new BooleanExpression(expression
3036: .getLeftExpression()));
3037: Label l0 = new Label();
3038: Label l2 = new Label();
3039: cv.visitJumpInsn(IFEQ, l0);
3040:
3041: cv.visitLabel(l2);
3042:
3043: visitConstantExpression(ConstantExpression.TRUE);
3044:
3045: Label l1 = new Label();
3046: cv.visitJumpInsn(GOTO, l1);
3047: cv.visitLabel(l0);
3048:
3049: visitBooleanExpression(new BooleanExpression(expression
3050: .getRightExpression()));
3051:
3052: cv.visitJumpInsn(IFNE, l2);
3053:
3054: visitConstantExpression(ConstantExpression.FALSE);
3055: cv.visitLabel(l1);
3056: }
3057:
3058: // todo: optimization: change to return primitive boolean. need to adjust the BinaryExpression and isComparisonExpression for
3059: // consistancy.
3060: protected void evaluateLogicalAndExpression(
3061: BinaryExpression expression) {
3062: visitBooleanExpression(new BooleanExpression(expression
3063: .getLeftExpression()));
3064: Label l0 = new Label();
3065: cv.visitJumpInsn(IFEQ, l0);
3066:
3067: visitBooleanExpression(new BooleanExpression(expression
3068: .getRightExpression()));
3069:
3070: cv.visitJumpInsn(IFEQ, l0);
3071:
3072: visitConstantExpression(ConstantExpression.TRUE);
3073:
3074: Label l1 = new Label();
3075: cv.visitJumpInsn(GOTO, l1);
3076: cv.visitLabel(l0);
3077:
3078: visitConstantExpression(ConstantExpression.FALSE);
3079:
3080: cv.visitLabel(l1);
3081: }
3082:
3083: protected void evaluateBinaryExpression(String method,
3084: BinaryExpression expression) {
3085: makeCall(expression.getLeftExpression(),
3086: new ConstantExpression(method),
3087: new ArgumentListExpression().addExpression(expression
3088: .getRightExpression()), invokeMethod, false,
3089: false, false);
3090: }
3091:
3092: protected void evaluateCompareTo(BinaryExpression expression) {
3093: Expression leftExpression = expression.getLeftExpression();
3094: leftExpression.visit(this );
3095: if (isComparisonExpression(leftExpression)) {
3096: helper.boxBoolean();
3097: }
3098:
3099: // if the right hand side is a boolean expression, we need to autobox
3100: Expression rightExpression = expression.getRightExpression();
3101: rightExpression.visit(this );
3102: if (isComparisonExpression(rightExpression)) {
3103: helper.boxBoolean();
3104: }
3105: compareToMethod.call(cv);
3106: }
3107:
3108: protected void evaluateBinaryExpressionWithAsignment(String method,
3109: BinaryExpression expression) {
3110: Expression leftExpression = expression.getLeftExpression();
3111: if (leftExpression instanceof BinaryExpression) {
3112: BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3113: if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3114: // lets replace this assignment to a subscript operator with a
3115: // method call
3116: // e.g. x[5] += 10
3117: // -> (x, [], 5), =, x[5] + 10
3118: // -> methodCall(x, "putAt", [5, methodCall(x[5], "plus", 10)])
3119:
3120: MethodCallExpression methodCall = new MethodCallExpression(
3121: expression.getLeftExpression(), method,
3122: new ArgumentListExpression(
3123: new Expression[] { expression
3124: .getRightExpression() }));
3125:
3126: Expression safeIndexExpr = createReusableExpression(leftBinExpr
3127: .getRightExpression());
3128:
3129: visitMethodCallExpression(new MethodCallExpression(
3130: leftBinExpr.getLeftExpression(), "putAt",
3131: new ArgumentListExpression(new Expression[] {
3132: safeIndexExpr, methodCall })));
3133: //cv.visitInsn(POP);
3134: return;
3135: }
3136: }
3137:
3138: evaluateBinaryExpression(method, expression);
3139:
3140: // br to leave a copy of rvalue on the stack. see also isPopRequired()
3141: cv.visitInsn(DUP);
3142:
3143: leftHandExpression = true;
3144: evaluateExpression(leftExpression);
3145: leftHandExpression = false;
3146: }
3147:
3148: private void evaluateBinaryExpression(MethodCaller compareMethod,
3149: BinaryExpression expression) {
3150: Expression leftExp = expression.getLeftExpression();
3151: Expression rightExp = expression.getRightExpression();
3152: load(leftExp);
3153: load(rightExp);
3154: compareMethod.call(cv);
3155: }
3156:
3157: protected void evaluateEqual(BinaryExpression expression) {
3158: Expression leftExpression = expression.getLeftExpression();
3159: if (leftExpression instanceof BinaryExpression) {
3160: BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
3161: if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
3162: // lets replace this assignment to a subscript operator with a
3163: // method call
3164: // e.g. x[5] = 10
3165: // -> (x, [], 5), =, 10
3166: // -> methodCall(x, "putAt", [5, 10])
3167:
3168: visitMethodCallExpression(new MethodCallExpression(
3169: leftBinExpr.getLeftExpression(), "putAt",
3170: new ArgumentListExpression(new Expression[] {
3171: leftBinExpr.getRightExpression(),
3172: expression.getRightExpression() })));
3173: // cv.visitInsn(POP); //this is realted to isPopRequired()
3174: return;
3175: }
3176: }
3177:
3178: // lets evaluate the RHS then hopefully the LHS will be a field
3179: Expression rightExpression = expression.getRightExpression();
3180: ClassNode type = getLHSType(leftExpression);
3181: // lets not cast for primitive types as we handle these in field setting etc
3182: if (ClassHelper.isPrimitiveType(type)) {
3183: visitAndAutoboxBoolean(rightExpression);
3184: } else if (type != ClassHelper.OBJECT_TYPE) {
3185: visitCastExpression(new CastExpression(type,
3186: rightExpression));
3187: } else {
3188: visitAndAutoboxBoolean(rightExpression);
3189: }
3190:
3191: cv.visitInsn(DUP); // to leave a copy of the rightexpression value on the stack after the assignment.
3192: leftHandExpression = true;
3193: leftExpression.visit(this );
3194: leftHandExpression = false;
3195: }
3196:
3197: /**
3198: * Deduces the type name required for some casting
3199: *
3200: * @return the type of the given (LHS) expression or null if it is java.lang.Object or it cannot be deduced
3201: */
3202: protected ClassNode getLHSType(Expression leftExpression) {
3203: if (leftExpression instanceof VariableExpression) {
3204: VariableExpression varExp = (VariableExpression) leftExpression;
3205: ClassNode type = varExp.getType();
3206: if (isValidTypeForCast(type)) {
3207: return type;
3208: }
3209: String variableName = varExp.getName();
3210: Variable variable = compileStack.getVariable(variableName,
3211: false);
3212: if (variable != null) {
3213: if (variable.isHolder()) {
3214: return type;
3215: }
3216: if (variable.isProperty())
3217: return variable.getType();
3218: type = variable.getType();
3219: if (isValidTypeForCast(type)) {
3220: return type;
3221: }
3222: } else {
3223: FieldNode field = classNode.getField(variableName);
3224: if (field == null) {
3225: field = classNode.getOuterField(variableName);
3226: }
3227: if (field != null) {
3228: type = field.getType();
3229: if (!field.isHolder() && isValidTypeForCast(type)) {
3230: return type;
3231: }
3232: }
3233: }
3234: } else if (leftExpression instanceof FieldExpression) {
3235: FieldExpression fieldExp = (FieldExpression) leftExpression;
3236: ClassNode type = fieldExp.getType();
3237: if (isValidTypeForCast(type)) {
3238: return type;
3239: }
3240: }
3241: return ClassHelper.DYNAMIC_TYPE;
3242: }
3243:
3244: protected boolean isValidTypeForCast(ClassNode type) {
3245: return type != ClassHelper.DYNAMIC_TYPE
3246: && type != ClassHelper.REFERENCE_TYPE;
3247: }
3248:
3249: protected void visitAndAutoboxBoolean(Expression expression) {
3250: expression.visit(this );
3251:
3252: if (isComparisonExpression(expression)) {
3253: helper.boxBoolean(); // convert boolean to Boolean
3254: }
3255: }
3256:
3257: protected void evaluatePrefixMethod(String method,
3258: Expression expression) {
3259: // execute method
3260: makeCall(expression, new ConstantExpression(method),
3261: MethodCallExpression.NO_ARGUMENTS, invokeMethod, false,
3262: false, false);
3263:
3264: // store
3265: leftHandExpression = true;
3266: expression.visit(this );
3267:
3268: // reload new value
3269: leftHandExpression = false;
3270: expression.visit(this );
3271: }
3272:
3273: protected void evaluatePostfixMethod(String method,
3274: Expression expression) {
3275: // load
3276: expression.visit(this );
3277:
3278: // save value for later
3279: int tempIdx = compileStack.defineTemporaryVariable("postfix_"
3280: + method, true);
3281:
3282: //execute method
3283: makeCall(expression, new ConstantExpression(method),
3284: MethodCallExpression.NO_ARGUMENTS, invokeMethod, false,
3285: false, false);
3286:
3287: // store
3288: leftHandExpression = true;
3289: expression.visit(this );
3290: leftHandExpression = false;
3291:
3292: //reload saved value
3293: cv.visitVarInsn(ALOAD, tempIdx);
3294: compileStack.removeVar(tempIdx);
3295: }
3296:
3297: protected void evaluateInstanceof(BinaryExpression expression) {
3298: visitAndAutoboxBoolean(expression.getLeftExpression());
3299: Expression rightExp = expression.getRightExpression();
3300: ClassNode classType = ClassHelper.DYNAMIC_TYPE;
3301: if (rightExp instanceof ClassExpression) {
3302: ClassExpression classExp = (ClassExpression) rightExp;
3303: classType = classExp.getType();
3304: } else {
3305: throw new RuntimeException(
3306: "Right hand side of the instanceof keyword must be a class name, not: "
3307: + rightExp);
3308: }
3309: String classInternalName = BytecodeHelper
3310: .getClassInternalName(classType);
3311: cv.visitTypeInsn(INSTANCEOF, classInternalName);
3312: }
3313:
3314: /**
3315: * @return true if the given argument expression requires the stack, in
3316: * which case the arguments are evaluated first, stored in the
3317: * variable stack and then reloaded to make a method call
3318: */
3319: protected boolean argumentsUseStack(Expression arguments) {
3320: return arguments instanceof TupleExpression
3321: || arguments instanceof ClosureExpression;
3322: }
3323:
3324: /**
3325: * @return true if the given expression represents a non-static field
3326: */
3327: protected boolean isNonStaticField(Expression expression) {
3328: FieldNode field = null;
3329: if (expression instanceof VariableExpression) {
3330: VariableExpression varExp = (VariableExpression) expression;
3331: field = classNode.getField(varExp.getName());
3332: } else if (expression instanceof FieldExpression) {
3333: FieldExpression fieldExp = (FieldExpression) expression;
3334: field = classNode.getField(fieldExp.getFieldName());
3335: } else if (expression.getClass() == PropertyExpression.class) {
3336: PropertyExpression fieldExp = (PropertyExpression) expression;
3337: String possibleField = fieldExp.getPropertyAsString();
3338: if (possibleField != null)
3339: field = classNode.getField(possibleField);
3340: }
3341: if (field != null) {
3342: return !field.isStatic();
3343: }
3344: return false;
3345: }
3346:
3347: private static boolean isThisExpression(Expression expression) {
3348: if (expression instanceof VariableExpression) {
3349: VariableExpression varExp = (VariableExpression) expression;
3350: return varExp.getName().equals("this");
3351: }
3352: return false;
3353: }
3354:
3355: private static boolean isSuperExpression(Expression expression) {
3356: if (expression instanceof VariableExpression) {
3357: VariableExpression varExp = (VariableExpression) expression;
3358: return varExp.getName().equals("super");
3359: }
3360: return false;
3361: }
3362:
3363: private static boolean isThisOrSuper(Expression expression) {
3364: return isThisExpression(expression)
3365: || isSuperExpression(expression);
3366: }
3367:
3368: /**
3369: * For assignment expressions, return a safe expression for the LHS we can use
3370: * to return the value
3371: */
3372: protected Expression createReturnLHSExpression(Expression expression) {
3373: if (expression instanceof BinaryExpression) {
3374: BinaryExpression binExpr = (BinaryExpression) expression;
3375: if (binExpr.getOperation().isA(Types.ASSIGNMENT_OPERATOR)) {
3376: return createReusableExpression(binExpr
3377: .getLeftExpression());
3378: }
3379: }
3380: return null;
3381: }
3382:
3383: protected Expression createReusableExpression(Expression expression) {
3384: ExpressionTransformer transformer = new ExpressionTransformer() {
3385: public Expression transform(Expression expression) {
3386: if (expression instanceof PostfixExpression) {
3387: PostfixExpression postfixExp = (PostfixExpression) expression;
3388: return postfixExp.getExpression();
3389: } else if (expression instanceof PrefixExpression) {
3390: PrefixExpression prefixExp = (PrefixExpression) expression;
3391: return prefixExp.getExpression();
3392: }
3393: return expression;
3394: }
3395: };
3396:
3397: // could just be a postfix / prefix expression or nested inside some other expression
3398: return transformer.transform(expression
3399: .transformExpression(transformer));
3400: }
3401:
3402: protected boolean isComparisonExpression(Expression expression) {
3403: if (expression instanceof BinaryExpression) {
3404: BinaryExpression binExpr = (BinaryExpression) expression;
3405: switch (binExpr.getOperation().getType()) {
3406: case Types.COMPARE_EQUAL:
3407: case Types.MATCH_REGEX:
3408: case Types.COMPARE_GREATER_THAN:
3409: case Types.COMPARE_GREATER_THAN_EQUAL:
3410: case Types.COMPARE_LESS_THAN:
3411: case Types.COMPARE_LESS_THAN_EQUAL:
3412: case Types.COMPARE_IDENTICAL:
3413: case Types.COMPARE_NOT_EQUAL:
3414: case Types.KEYWORD_INSTANCEOF:
3415: case Types.KEYWORD_IN:
3416: return true;
3417: }
3418: } else if (expression instanceof BooleanExpression) {
3419: return true;
3420: }
3421: return false;
3422: }
3423:
3424: protected void onLineNumber(ASTNode statement, String message) {
3425: int line = statement.getLineNumber();
3426: int col = statement.getColumnNumber();
3427: this .currentASTNode = statement;
3428:
3429: if (line >= 0) {
3430: lineNumber = line;
3431: columnNumber = col;
3432: }
3433: if (CREATE_LINE_NUMBER_INFO && line >= 0 && cv != null) {
3434: Label l = new Label();
3435: cv.visitLabel(l);
3436: cv.visitLineNumber(line, l);
3437: if (ASM_DEBUG) {
3438: helper.mark(message + "[" + statement.getLineNumber()
3439: + ":" + statement.getColumnNumber() + "]");
3440: }
3441: }
3442: }
3443:
3444: private boolean isInnerClass() {
3445: return classNode instanceof InnerClassNode;
3446: }
3447:
3448: /** @return true if the given name is a local variable or a field */
3449: protected boolean isFieldOrVariable(String name) {
3450: return compileStack.containsVariable(name)
3451: || classNode.getField(name) != null;
3452: }
3453:
3454: /**
3455: * @return if the type of the expression can be determined at compile time
3456: * then this method returns the type - otherwise null
3457: */
3458: protected ClassNode getExpressionType(Expression expression) {
3459: if (isComparisonExpression(expression)) {
3460: return ClassHelper.boolean_TYPE;
3461: }
3462: if (expression instanceof VariableExpression) {
3463: if (expression == VariableExpression.THIS_EXPRESSION) {
3464: return classNode;
3465: } else if (expression == VariableExpression.SUPER_EXPRESSION) {
3466: return classNode.getSuperClass();
3467: }
3468:
3469: VariableExpression varExpr = (VariableExpression) expression;
3470: Variable variable = compileStack.getVariable(varExpr
3471: .getName(), false);
3472: if (variable != null && !variable.isHolder()) {
3473: ClassNode type = variable.getType();
3474: if (!variable.isDynamicTyped())
3475: return type;
3476: }
3477: if (variable == null) {
3478: org.codehaus.groovy.ast.Variable var = (org.codehaus.groovy.ast.Variable) compileStack
3479: .getScope().getReferencedClassVariables().get(
3480: varExpr.getName());
3481: if (var != null && !var.isDynamicTyped())
3482: return var.getType();
3483: }
3484: }
3485: return expression.getType();
3486: }
3487:
3488: protected boolean isInClosureConstructor() {
3489: return constructorNode != null
3490: && classNode.getOuterClass() != null
3491: && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
3492: }
3493:
3494: protected boolean isInClosure() {
3495: return classNode.getOuterClass() != null
3496: && classNode.getSuperClass() == ClassHelper.CLOSURE_TYPE;
3497: }
3498:
3499: protected boolean isStaticMethod() {
3500: if (methodNode == null) { // we're in a constructor
3501: return false;
3502: }
3503: return methodNode.isStatic();
3504: }
3505:
3506: protected CompileUnit getCompileUnit() {
3507: CompileUnit answer = classNode.getCompileUnit();
3508: if (answer == null) {
3509: answer = context.getCompileUnit();
3510: }
3511: return answer;
3512: }
3513:
3514: protected boolean isHolderVariable(Expression expression) {
3515: if (expression instanceof FieldExpression) {
3516: FieldExpression fieldExp = (FieldExpression) expression;
3517: return fieldExp.getField().isHolder();
3518: }
3519: if (expression instanceof VariableExpression) {
3520: VariableExpression varExp = (VariableExpression) expression;
3521: Variable variable = compileStack.getVariable(varExp
3522: .getName(), false);
3523: if (variable != null) {
3524: return variable.isHolder();
3525: }
3526: FieldNode field = classNode.getField(varExp.getName());
3527: if (field != null) {
3528: return field.isHolder();
3529: }
3530: }
3531: return false;
3532: }
3533:
3534: public static boolean usesSuper(MethodCallExpression call) {
3535: Expression expression = call.getObjectExpression();
3536: if (expression instanceof VariableExpression) {
3537: VariableExpression varExp = (VariableExpression) expression;
3538: String variable = varExp.getName();
3539: return variable.equals("super");
3540: }
3541: return false;
3542: }
3543:
3544: public static boolean usesSuper(PropertyExpression pe) {
3545: Expression expression = pe.getObjectExpression();
3546: if (expression instanceof VariableExpression) {
3547: VariableExpression varExp = (VariableExpression) expression;
3548: String variable = varExp.getName();
3549: return variable.equals("super");
3550: }
3551: return false;
3552: }
3553: }
|