protected void evaluateArrayAssignmentWithOperator( String method, BinaryExpression expression, BinaryExpression leftBinExpr) { CompileStack compileStack = getController().getCompileStack(); AsmClassGenerator acg = getController().getAcg(); OperandStack os = getController().getOperandStack(); // e.g. x[a] += b // to avoid loading x and a twice we transform the expression to use // ExpressionAsVariableSlot // -> subscript=a, receiver=x, receiver[subscript]+b, =, receiver[subscript] // -> subscript=a, receiver=x, receiver#getAt(subscript)#plus(b), =, receiver#putAt(subscript) // -> subscript=a, receiver=x, receiver#putAt(subscript, receiver#getAt(subscript)#plus(b)) // the result of x[a] += b is x[a]+b, thus: // -> subscript=a, receiver=x, receiver#putAt(subscript, ret=receiver#getAt(subscript)#plus(b)), // ret ExpressionAsVariableSlot subscript = new ExpressionAsVariableSlot(controller, leftBinExpr.getRightExpression(), "subscript"); ExpressionAsVariableSlot receiver = new ExpressionAsVariableSlot(controller, leftBinExpr.getLeftExpression(), "receiver"); MethodCallExpression getAt = new MethodCallExpression(receiver, "getAt", new ArgumentListExpression(subscript)); MethodCallExpression operation = new MethodCallExpression(getAt, method, expression.getRightExpression()); ExpressionAsVariableSlot ret = new ExpressionAsVariableSlot(controller, operation, "ret"); MethodCallExpression putAt = new MethodCallExpression(receiver, "putAt", new ArgumentListExpression(subscript, ret)); putAt.visit(acg); os.pop(); os.load(ret.getType(), ret.getIndex()); compileStack.removeVar(ret.getIndex()); compileStack.removeVar(subscript.getIndex()); compileStack.removeVar(receiver.getIndex()); }
public static void loadReference(String name, WriterController controller) { CompileStack compileStack = controller.getCompileStack(); MethodVisitor mv = controller.getMethodVisitor(); ClassNode classNode = controller.getClassNode(); AsmClassGenerator acg = controller.getAcg(); // compileStack.containsVariable(name) means to ask if the variable is already declared // compileStack.getScope().isReferencedClassVariable(name) means to ask if the variable is a // field // If it is no field and is not yet declared, then it is either a closure shared variable or // an already declared variable. if (!compileStack.containsVariable(name) && compileStack.getScope().isReferencedClassVariable(name)) { acg.visitFieldExpression(new FieldExpression(classNode.getDeclaredField(name))); } else { BytecodeVariable v = compileStack.getVariable(name, !classNodeUsesReferences(controller.getClassNode())); if (v == null) { // variable is not on stack because we are // inside a nested Closure and this variable // was not used before // then load it from the Closure field FieldNode field = classNode.getDeclaredField(name); mv.visitVarInsn(ALOAD, 0); mv.visitFieldInsn( GETFIELD, controller.getInternalClassName(), name, BytecodeHelper.getTypeDescription(field.getType())); } else { mv.visitVarInsn(ALOAD, v.getIndex()); } controller.getOperandStack().push(ClassHelper.REFERENCE_TYPE); } }
public void loadOrStoreVariable(BytecodeVariable variable, boolean useReferenceDirectly) { CompileStack compileStack = controller.getCompileStack(); if (compileStack.isLHS()) { storeVar(variable); } else { MethodVisitor mv = controller.getMethodVisitor(); int idx = variable.getIndex(); ClassNode type = variable.getType(); if (variable.isHolder()) { mv.visitVarInsn(ALOAD, idx); if (!useReferenceDirectly) { mv.visitMethodInsn( INVOKEVIRTUAL, "groovy/lang/Reference", "get", "()Ljava/lang/Object;", false); BytecodeHelper.doCast(mv, type); push(type); } else { push(ClassHelper.REFERENCE_TYPE); } } else { load(type, idx); } } }
private void evaluateElvisOperatorExpression(ElvisOperatorExpression expression) { MethodVisitor mv = controller.getMethodVisitor(); CompileStack compileStack = controller.getCompileStack(); OperandStack operandStack = controller.getOperandStack(); TypeChooser typeChooser = controller.getTypeChooser(); Expression boolPart = expression.getBooleanExpression().getExpression(); Expression falsePart = expression.getFalseExpression(); ClassNode truePartType = typeChooser.resolveType(boolPart, controller.getClassNode()); ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode()); ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType); // x?:y is equal to x?x:y, which evals to // var t=x; boolean(t)?t:y // first we load x, dup it, convert the dupped to boolean, then // jump depending on the value. For true we are done, for false we // have to load y, thus we first remove x and then load y. // But since x and y may have different stack lengths, this cannot work // Thus we have to have to do the following: // Be X the type of x, Y the type of y and S the common supertype of // X and Y, then we have to see x?:y as // var t=x;boolean(t)?S(t):S(y) // so we load x, dup it, store the value in a local variable (t), then // do boolean conversion. In the true part load t and cast it to S, // in the false part load y and cast y to S // load x, dup it, store one in $t and cast the remaining one to boolean int mark = operandStack.getStackLength(); boolPart.visit(controller.getAcg()); operandStack.dup(); if (ClassHelper.isPrimitiveType(truePartType) && !ClassHelper.isPrimitiveType(operandStack.getTopOperand())) { truePartType = ClassHelper.getWrapper(truePartType); } int retValueId = compileStack.defineTemporaryVariable("$t", truePartType, true); operandStack.castToBool(mark, true); Label l0 = operandStack.jump(IFEQ); // true part: load $t and cast to S operandStack.load(truePartType, retValueId); operandStack.doGroovyCast(common); Label l1 = new Label(); mv.visitJumpInsn(GOTO, l1); // false part: load false expression and cast to S mv.visitLabel(l0); falsePart.visit(controller.getAcg()); operandStack.doGroovyCast(common); // finish and cleanup mv.visitLabel(l1); compileStack.removeVar(retValueId); controller.getOperandStack().replace(common, 2); }
protected void evaluateBinaryExpression(String message, BinaryExpression binExp) { CompileStack compileStack = controller.getCompileStack(); Expression receiver = binExp.getLeftExpression(); Expression arguments = binExp.getRightExpression(); // ensure VariableArguments are read, not stored compileStack.pushLHS(false); controller.getInvocationWriter().makeSingleArgumentCall(receiver, message, arguments); compileStack.popLHS(); }
public void writeClosure(ClosureExpression expression) { CompileStack compileStack = controller.getCompileStack(); MethodVisitor mv = controller.getMethodVisitor(); ClassNode classNode = controller.getClassNode(); AsmClassGenerator acg = controller.getAcg(); // generate closure as public class to make sure it can be properly invoked by classes of the // Groovy runtime without circumventing JVM access checks (see CachedMethod for example). ClassNode closureClass = getOrAddClosureClass(expression, ACC_PUBLIC); String closureClassinternalName = BytecodeHelper.getClassInternalName(closureClass); List constructors = closureClass.getDeclaredConstructors(); ConstructorNode node = (ConstructorNode) constructors.get(0); Parameter[] localVariableParams = node.getParameters(); mv.visitTypeInsn(NEW, closureClassinternalName); mv.visitInsn(DUP); if (controller.isStaticMethod() || compileStack.isInSpecialConstructorCall()) { (new ClassExpression(classNode)).visit(acg); (new ClassExpression(controller.getOutermostClass())).visit(acg); } else { mv.visitVarInsn(ALOAD, 0); controller.getOperandStack().push(ClassHelper.OBJECT_TYPE); loadThis(); } // now let's load the various parameters we're passing // we start at index 2 because the first variable we pass // is the owner instance and at this point it is already // on the stack for (int i = 2; i < localVariableParams.length; i++) { Parameter param = localVariableParams[i]; String name = param.getName(); loadReference(name, controller); if (param.getNodeMetaData(ClosureWriter.UseExistingReference.class) == null) { param.setNodeMetaData(ClosureWriter.UseExistingReference.class, Boolean.TRUE); } } // we may need to pass in some other constructors // cv.visitMethodInsn(INVOKESPECIAL, innerClassinternalName, "<init>", prototype + ")V"); mv.visitMethodInsn( INVOKESPECIAL, closureClassinternalName, "<init>", BytecodeHelper.getMethodDescriptor(ClassHelper.VOID_TYPE, localVariableParams), false); controller.getOperandStack().replace(ClassHelper.CLOSURE_TYPE, localVariableParams.length); }
boolean makeGetField( final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean implicitThis, final boolean samePackage) { FieldNode field = receiverType.getField(fieldName); // direct access is allowed if we are in the same class as the declaring class // or we are in an inner class if (field != null && isDirectAccessAllowed(field, controller.getClassNode(), samePackage)) { CompileStack compileStack = controller.getCompileStack(); MethodVisitor mv = controller.getMethodVisitor(); if (field.isStatic()) { mv.visitFieldInsn( GETSTATIC, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(field.getOriginType())); controller.getOperandStack().push(field.getOriginType()); } else { if (implicitThis) { compileStack.pushImplicitThis(implicitThis); } receiver.visit(controller.getAcg()); if (implicitThis) compileStack.popImplicitThis(); if (!controller.getOperandStack().getTopOperand().isDerivedFrom(field.getOwner())) { mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(field.getOwner())); } mv.visitFieldInsn( GETFIELD, BytecodeHelper.getClassInternalName(field.getOwner()), fieldName, BytecodeHelper.getTypeDescription(field.getOriginType())); } controller.getOperandStack().replace(field.getOriginType()); return true; } ClassNode superClass = receiverType.getSuperClass(); if (superClass != null) { return makeGetField(receiver, superClass, fieldName, implicitThis, false); } return false; }
private void execMethodAndStoreForSubscriptOperator( int op, String method, Expression expression, VariableSlotLoader usesSubscript, Expression orig) { final OperandStack operandStack = controller.getOperandStack(); writePostOrPrefixMethod(op, method, expression, orig); // we need special code for arrays to store the result (like for a[1]++) if (usesSubscript != null) { CompileStack compileStack = controller.getCompileStack(); BinaryExpression be = (BinaryExpression) expression; ClassNode methodResultType = operandStack.getTopOperand(); final int resultIdx = compileStack.defineTemporaryVariable("postfix_" + method, methodResultType, true); BytecodeExpression methodResultLoader = new VariableSlotLoader(methodResultType, resultIdx, operandStack); // execute the assignment, this will leave the right side // (here the method call result) on the stack assignToArray(be, be.getLeftExpression(), usesSubscript, methodResultLoader); compileStack.removeVar(resultIdx); } // here we handle a.b++ and a++ else if (expression instanceof VariableExpression || expression instanceof FieldExpression || expression instanceof PropertyExpression) { operandStack.dup(); controller.getCompileStack().pushLHS(true); expression.visit(controller.getAcg()); controller.getCompileStack().popLHS(); } // other cases don't need storing, so nothing to be done for them }
private void evaluatePostfixMethod( int op, String method, Expression expression, Expression orig) { CompileStack compileStack = controller.getCompileStack(); final OperandStack operandStack = controller.getOperandStack(); // load Expressions VariableSlotLoader usesSubscript = loadWithSubscript(expression); // save copy for later operandStack.dup(); ClassNode expressionType = operandStack.getTopOperand(); int tempIdx = compileStack.defineTemporaryVariable("postfix_" + method, expressionType, true); // execute Method execMethodAndStoreForSubscriptOperator(op, method, expression, usesSubscript, orig); // remove the result of the method call operandStack.pop(); // reload saved value operandStack.load(expressionType, tempIdx); compileStack.removeVar(tempIdx); if (usesSubscript != null) compileStack.removeVar(usesSubscript.getIndex()); }
private void writeListDotProperty( final Expression receiver, final String methodName, final MethodVisitor mv, final boolean safe) { ClassNode componentType = (ClassNode) receiver.getNodeMetaData(StaticCompilationMetadataKeys.COMPONENT_TYPE); if (componentType == null) { componentType = OBJECT_TYPE; } // for lists, replace list.foo with: // def result = new ArrayList(list.size()) // for (e in list) { result.add (e.foo) } // result CompileStack compileStack = controller.getCompileStack(); Label exit = new Label(); if (safe) { receiver.visit(controller.getAcg()); Label doGet = new Label(); mv.visitJumpInsn(IFNONNULL, doGet); controller.getOperandStack().remove(1); mv.visitInsn(ACONST_NULL); mv.visitJumpInsn(GOTO, exit); mv.visitLabel(doGet); } Variable tmpList = new VariableExpression("tmpList", make(ArrayList.class)); int var = compileStack.defineTemporaryVariable(tmpList, false); Variable iterator = new VariableExpression("iterator", Iterator_TYPE); int it = compileStack.defineTemporaryVariable(iterator, false); Variable nextVar = new VariableExpression("next", componentType); final int next = compileStack.defineTemporaryVariable(nextVar, false); mv.visitTypeInsn(NEW, "java/util/ArrayList"); mv.visitInsn(DUP); receiver.visit(controller.getAcg()); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "size", "()I", true); controller.getOperandStack().remove(1); mv.visitMethodInsn(INVOKESPECIAL, "java/util/ArrayList", "<init>", "(I)V", false); mv.visitVarInsn(ASTORE, var); Label l1 = new Label(); mv.visitLabel(l1); receiver.visit(controller.getAcg()); mv.visitMethodInsn( INVOKEINTERFACE, "java/util/List", "iterator", "()Ljava/util/Iterator;", true); controller.getOperandStack().remove(1); mv.visitVarInsn(ASTORE, it); Label l2 = new Label(); mv.visitLabel(l2); mv.visitVarInsn(ALOAD, it); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "hasNext", "()Z", true); Label l3 = new Label(); mv.visitJumpInsn(IFEQ, l3); mv.visitVarInsn(ALOAD, it); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/Iterator", "next", "()Ljava/lang/Object;", true); mv.visitTypeInsn(CHECKCAST, BytecodeHelper.getClassInternalName(componentType)); mv.visitVarInsn(ASTORE, next); Label l4 = new Label(); mv.visitLabel(l4); mv.visitVarInsn(ALOAD, var); final ClassNode finalComponentType = componentType; PropertyExpression pexp = new PropertyExpression( new BytecodeExpression() { @Override public void visit(final MethodVisitor mv) { mv.visitVarInsn(ALOAD, next); } @Override public ClassNode getType() { return finalComponentType; } }, methodName); pexp.visit(controller.getAcg()); controller.getOperandStack().box(); controller.getOperandStack().remove(1); mv.visitMethodInsn(INVOKEINTERFACE, "java/util/List", "add", "(Ljava/lang/Object;)Z", true); mv.visitInsn(POP); Label l5 = new Label(); mv.visitLabel(l5); mv.visitJumpInsn(GOTO, l2); mv.visitLabel(l3); mv.visitVarInsn(ALOAD, var); if (safe) { mv.visitLabel(exit); } controller.getOperandStack().push(make(ArrayList.class)); controller.getCompileStack().removeVar(next); controller.getCompileStack().removeVar(it); controller.getCompileStack().removeVar(var); }
public void evaluateEqual(BinaryExpression expression, boolean defineVariable) { AsmClassGenerator acg = controller.getAcg(); CompileStack compileStack = controller.getCompileStack(); OperandStack operandStack = controller.getOperandStack(); Expression rightExpression = expression.getRightExpression(); Expression leftExpression = expression.getLeftExpression(); ClassNode lhsType = controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode()); if (defineVariable && rightExpression instanceof EmptyExpression && !(leftExpression instanceof TupleExpression)) { VariableExpression ve = (VariableExpression) leftExpression; BytecodeVariable var = compileStack.defineVariable( ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false); operandStack.loadOrStoreVariable(var, false); return; } // let's evaluate the RHS and store the result ClassNode rhsType; if (rightExpression instanceof ListExpression && lhsType.isArray()) { ListExpression list = (ListExpression) rightExpression; ArrayExpression array = new ArrayExpression(lhsType.getComponentType(), list.getExpressions()); array.setSourcePosition(list); array.visit(acg); } else if (rightExpression instanceof EmptyExpression) { rhsType = leftExpression.getType(); loadInitValue(rhsType); } else { rightExpression.visit(acg); } rhsType = operandStack.getTopOperand(); boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression); int rhsValueId; if (directAssignment) { VariableExpression var = (VariableExpression) leftExpression; if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) { // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed rhsType = ClassHelper.getWrapper(rhsType); operandStack.box(); } // ensure we try to unbox null to cause a runtime NPE in case we assign // null to a primitive typed variable, even if it is used only in boxed // form as it is closure shared if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(var.getOriginType()) && isNull(rightExpression)) { operandStack.doGroovyCast(var.getOriginType()); // these two are never reached in bytecode and only there // to avoid verifyerrors and compiler infrastructure hazzle operandStack.box(); operandStack.doGroovyCast(lhsType); } // normal type transformation if (!ClassHelper.isPrimitiveType(lhsType) && isNull(rightExpression)) { operandStack.replace(lhsType); } else { operandStack.doGroovyCast(lhsType); } rhsType = lhsType; rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex(); } else { rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true); } // TODO: if rhs is VariableSlotLoader already, then skip crating a new one BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType, rhsValueId, operandStack); // assignment for subscript if (leftExpression instanceof BinaryExpression) { BinaryExpression leftBinExpr = (BinaryExpression) leftExpression; if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) { assignToArray( expression, leftBinExpr.getLeftExpression(), leftBinExpr.getRightExpression(), rhsValueLoader); } compileStack.removeVar(rhsValueId); return; } compileStack.pushLHS(true); // multiple declaration if (leftExpression instanceof TupleExpression) { TupleExpression tuple = (TupleExpression) leftExpression; int i = 0; for (Expression e : tuple.getExpressions()) { VariableExpression var = (VariableExpression) e; MethodCallExpression call = new MethodCallExpression( rhsValueLoader, "getAt", new ArgumentListExpression(new ConstantExpression(i))); call.visit(acg); i++; if (defineVariable) { operandStack.doGroovyCast(var); compileStack.defineVariable(var, true); operandStack.remove(1); } else { acg.visitVariableExpression(var); } } } // single declaration else if (defineVariable) { rhsValueLoader.visit(acg); operandStack.remove(1); compileStack.popLHS(); return; } // normal assignment else { int mark = operandStack.getStackLength(); // to leave a copy of the rightExpression value on the stack after the assignment. rhsValueLoader.visit(acg); TypeChooser typeChooser = controller.getTypeChooser(); ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode()); operandStack.doGroovyCast(targetType); leftExpression.visit(acg); operandStack.remove(operandStack.getStackLength() - mark); } compileStack.popLHS(); // return value of assignment rhsValueLoader.visit(acg); compileStack.removeVar(rhsValueId); }
private boolean doAssignmentToArray(BinaryExpression binExp) { if (!isAssignmentToArray(binExp)) return false; // we need to handle only assignment to arrays combined with an operation // special here. e.g x[a] += b int operation = removeAssignment(binExp.getOperation().getType()); ClassNode current = getController().getClassNode(); Expression leftExp = binExp.getLeftExpression(); ClassNode leftType = getController().getTypeChooser().resolveType(leftExp, current); Expression rightExp = binExp.getRightExpression(); ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current); int operationType = getOperandType(leftType); BinaryExpressionWriter bew = binExpWriter[operationType]; boolean simulationSuccess = bew.arrayGet(LEFT_SQUARE_BRACKET, true); simulationSuccess = simulationSuccess && bew.write(operation, true); simulationSuccess = simulationSuccess && bew.arraySet(true); if (!simulationSuccess) return false; AsmClassGenerator acg = getController().getAcg(); OperandStack operandStack = getController().getOperandStack(); CompileStack compileStack = getController().getCompileStack(); // for x[a] += b we have the structure: // x = left(left(binExp))), b = right(binExp), a = right(left(binExp))) // for array set we need these values on stack: array, index, right // for array get we need these values on stack: array, index // to eval the expression we need x[a] = x[a]+b // -> arraySet(x,a, x[a]+b) // -> arraySet(x,a, arrayGet(x,a,b)) // --> x,a, x,a, b as operands // --> load x, load a, DUP2, call arrayGet, load b, call operation,call arraySet // since we cannot DUP2 here easily we will save the subscript and DUP x // --> sub=a, load x, DUP, load sub, call arrayGet, load b, call operation, load sub, call // arraySet BinaryExpression arrayWithSubscript = (BinaryExpression) leftExp; Expression subscript = arrayWithSubscript.getRightExpression(); // load array index: sub=a [load x, DUP, load sub, call arrayGet, load b, call operation, load // sub, call arraySet] subscript.visit(acg); operandStack.doGroovyCast(int_TYPE); int subscriptValueId = compileStack.defineTemporaryVariable("$sub", ClassHelper.int_TYPE, true); // load array: load x and DUP [load sub, call arrayGet, load b, call operation, load sub, call // arraySet] arrayWithSubscript.getLeftExpression().visit(acg); operandStack.doGroovyCast(leftType.makeArray()); operandStack.dup(); // array get: load sub, call arrayGet [load b, call operation, load sub, call arraySet] operandStack.load(ClassHelper.int_TYPE, subscriptValueId); bew.arrayGet(LEFT_SQUARE_BRACKET, false); operandStack.replace(leftType, 2); // complete rhs: load b, call operation [load sub, call arraySet] binExp.getRightExpression().visit(acg); if (!(bew instanceof BinaryObjectExpressionHelper)) { // in primopts we convert to the left type for supported binary operations operandStack.doGroovyCast(leftType); } bew.write(operation, false); // let us save that value for the return operandStack.dup(); int resultValueId = compileStack.defineTemporaryVariable("$result", rightType, true); // array set: load sub, call arraySet [] operandStack.load(ClassHelper.int_TYPE, subscriptValueId); operandStack.swap(); bew.arraySet(false); operandStack.remove(3); // 3 operands, the array, the index and the value! // load return value operandStack.load(rightType, resultValueId); // cleanup compileStack.removeVar(resultValueId); compileStack.removeVar(subscriptValueId); return true; }