public void storeVar(BytecodeVariable variable) { MethodVisitor mv = controller.getMethodVisitor(); int idx = variable.getIndex(); ClassNode type = variable.getType(); // value is on stack if (variable.isHolder()) { doGroovyCast(type); box(); mv.visitVarInsn(ALOAD, idx); mv.visitTypeInsn(CHECKCAST, "groovy/lang/Reference"); mv.visitInsn(SWAP); mv.visitMethodInsn( INVOKEVIRTUAL, "groovy/lang/Reference", "set", "(Ljava/lang/Object;)V", false); } else { doGroovyCast(type); if (type == ClassHelper.double_TYPE) { mv.visitVarInsn(DSTORE, idx); } else if (type == ClassHelper.float_TYPE) { mv.visitVarInsn(FSTORE, idx); } else if (type == ClassHelper.long_TYPE) { mv.visitVarInsn(LSTORE, idx); } else if (type == ClassHelper.boolean_TYPE || type == ClassHelper.char_TYPE || type == ClassHelper.byte_TYPE || type == ClassHelper.int_TYPE || type == ClassHelper.short_TYPE) { mv.visitVarInsn(ISTORE, idx); } else { mv.visitVarInsn(ASTORE, idx); } } // remove RHS value from operand stack remove(1); }
@Override protected void assignToArray( Expression orig, Expression receiver, Expression index, Expression rhsValueLoader) { ClassNode current = getController().getClassNode(); ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current); ClassNode arrayComponentType = arrayType.getComponentType(); int operationType = getOperandType(arrayComponentType); BinaryExpressionWriter bew = binExpWriter[operationType]; AsmClassGenerator acg = getController().getAcg(); if (bew.arraySet(true) && arrayType.isArray()) { OperandStack operandStack = getController().getOperandStack(); // load the array receiver.visit(acg); operandStack.doGroovyCast(arrayType); // load index index.visit(acg); operandStack.doGroovyCast(int_TYPE); // load rhs rhsValueLoader.visit(acg); operandStack.doGroovyCast(arrayComponentType); // store value in array bew.arraySet(false); // load return value && correct operand stack stack operandStack.remove(3); rhsValueLoader.visit(acg); } else { super.assignToArray(orig, receiver, index, rhsValueLoader); } }
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); }
private void writePowerCall( Expression receiver, Expression arguments, final ClassNode rType, ClassNode aType) { OperandStack operandStack = controller.getOperandStack(); int m1 = operandStack.getStackLength(); // slow Path prepareSiteAndReceiver(receiver, "power", false, controller.getCompileStack().isLHS()); operandStack.doGroovyCast(getWrapper(rType)); visitBoxedArgument(arguments); operandStack.doGroovyCast(getWrapper(aType)); int m2 = operandStack.getStackLength(); MethodVisitor mv = controller.getMethodVisitor(); if (BigDecimal_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) { mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/math/BigDecimal;Ljava/lang/Integer;)Ljava/lang/Number;", false); } else if (BigInteger_TYPE.equals(rType) && Integer_TYPE.equals(getWrapper(aType))) { mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/math/BigInteger;Ljava/lang/Integer;)Ljava/lang/Number;", false); } else if (Long_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) { mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Long;Ljava/lang/Integer;)Ljava/lang/Number;", false); } else if (Integer_TYPE.equals(getWrapper(rType)) && Integer_TYPE.equals(getWrapper(aType))) { mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Integer;Ljava/lang/Integer;)Ljava/lang/Number;", false); } else { mv.visitMethodInsn( INVOKESTATIC, "org/codehaus/groovy/runtime/DefaultGroovyMethods", "power", "(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;", false); } controller.getOperandStack().replace(Number_TYPE, m2 - m1); }
protected boolean doPrimitiveCompare( ClassNode leftType, ClassNode rightType, BinaryExpression binExp) { Expression leftExp = binExp.getLeftExpression(); Expression rightExp = binExp.getRightExpression(); int operation = binExp.getOperation().getType(); int operationType = getOperandConversionType(leftType, rightType); BinaryExpressionWriter bew = binExpWriter[operationType]; if (!bew.write(operation, true)) return false; AsmClassGenerator acg = getController().getAcg(); OperandStack os = getController().getOperandStack(); leftExp.visit(acg); os.doGroovyCast(bew.getNormalOpResultType()); rightExp.visit(acg); os.doGroovyCast(bew.getNormalOpResultType()); bew.write(operation, false); return true; }
private void evaluateNormalTernary(TernaryExpression expression) { MethodVisitor mv = controller.getMethodVisitor(); OperandStack operandStack = controller.getOperandStack(); TypeChooser typeChooser = controller.getTypeChooser(); Expression boolPart = expression.getBooleanExpression(); Expression truePart = expression.getTrueExpression(); Expression falsePart = expression.getFalseExpression(); ClassNode truePartType = typeChooser.resolveType(truePart, controller.getClassNode()); ClassNode falsePartType = typeChooser.resolveType(falsePart, controller.getClassNode()); ClassNode common = WideningCategories.lowestUpperBound(truePartType, falsePartType); // we compile b?x:y as // boolean(b)?S(x):S(y), S = common super type of x,y // so we load b, do boolean conversion. // In the true part load x and cast it to S, // in the false part load y and cast y to S // load b and convert to boolean int mark = operandStack.getStackLength(); boolPart.visit(controller.getAcg()); operandStack.castToBool(mark, true); Label l0 = operandStack.jump(IFEQ); // true part: load x and cast to S truePart.visit(controller.getAcg()); operandStack.doGroovyCast(common); Label l1 = new Label(); mv.visitJumpInsn(GOTO, l1); // false part: load y and cast to S mv.visitLabel(l0); falsePart.visit(controller.getAcg()); operandStack.doGroovyCast(common); // finish and cleanup mv.visitLabel(l1); controller.getOperandStack().replace(common, 2); }
private void evaluateLogicalAndExpression(BinaryExpression expression) { MethodVisitor mv = controller.getMethodVisitor(); AsmClassGenerator acg = controller.getAcg(); OperandStack operandStack = controller.getOperandStack(); expression.getLeftExpression().visit(acg); operandStack.doGroovyCast(ClassHelper.boolean_TYPE); Label falseCase = operandStack.jump(IFEQ); expression.getRightExpression().visit(acg); operandStack.doGroovyCast(ClassHelper.boolean_TYPE); operandStack.jump(IFEQ, falseCase); ConstantExpression.PRIM_TRUE.visit(acg); Label trueCase = new Label(); mv.visitJumpInsn(GOTO, trueCase); mv.visitLabel(falseCase); ConstantExpression.PRIM_FALSE.visit(acg); mv.visitLabel(trueCase); operandStack.remove(1); // have to remove 1 because of the GOTO }
private void evaluateLogicalOrExpression(BinaryExpression expression) { MethodVisitor mv = controller.getMethodVisitor(); AsmClassGenerator acg = controller.getAcg(); OperandStack operandStack = controller.getOperandStack(); Label end = new Label(); expression.getLeftExpression().visit(acg); operandStack.doGroovyCast(ClassHelper.boolean_TYPE); Label trueCase = operandStack.jump(IFNE); expression.getRightExpression().visit(acg); operandStack.doGroovyCast(ClassHelper.boolean_TYPE); Label falseCase = operandStack.jump(IFEQ); mv.visitLabel(trueCase); ConstantExpression.PRIM_TRUE.visit(acg); operandStack.jump(GOTO, end); mv.visitLabel(falseCase); ConstantExpression.PRIM_FALSE.visit(acg); mv.visitLabel(end); }
private void writeArrayGet( final Expression receiver, final Expression arguments, final ClassNode rType, final ClassNode aType) { OperandStack operandStack = controller.getOperandStack(); int m1 = operandStack.getStackLength(); // visit receiver receiver.visit(controller.getAcg()); // visit arguments as array index arguments.visit(controller.getAcg()); operandStack.doGroovyCast(int_TYPE); int m2 = operandStack.getStackLength(); // array access controller.getMethodVisitor().visitInsn(AALOAD); operandStack.replace(rType.getComponentType(), m2 - m1); }
@Override protected void writePostOrPrefixMethod( int op, String method, Expression expression, Expression orig) { ClassNode type = getController().getTypeChooser().resolveType(orig, getController().getClassNode()); int operationType = getOperandType(type); BinaryExpressionWriter bew = binExpWriter[operationType]; if (bew.writePostOrPrefixMethod(op, true)) { OperandStack operandStack = getController().getOperandStack(); // at this point the receiver will be already on the stack operandStack.doGroovyCast(type); bew.writePostOrPrefixMethod(op, false); operandStack.replace(bew.getNormalOpResultType()); } else { super.writePostOrPrefixMethod(op, method, expression, orig); } }
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; }
@Override protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) { int operation = removeAssignment(binExp.getOperation().getType()); ClassNode current = getController().getClassNode(); Expression leftExp = binExp.getLeftExpression(); ClassNode leftTypeOrig = getController().getTypeChooser().resolveType(leftExp, current); ClassNode leftType = leftTypeOrig; Expression rightExp = binExp.getRightExpression(); ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current); AsmClassGenerator acg = getController().getAcg(); OperandStack os = getController().getOperandStack(); if (operation == LEFT_SQUARE_BRACKET) { leftType = leftTypeOrig.getComponentType(); int operationType = getOperandType(leftType); BinaryExpressionWriter bew = binExpWriter[operationType]; if (leftTypeOrig.isArray() && isIntCastableType(rightExp) && bew.arrayGet(operation, true)) { leftExp.visit(acg); os.doGroovyCast(leftTypeOrig); rightExp.visit(acg); os.doGroovyCast(int_TYPE); bew.arrayGet(operation, false); os.replace(bew.getArrayGetResultType(), 2); } else { super.evaluateBinaryExpression(message, binExp); } } else if (operation == DIVIDE) { int operationType = getOperandType(getController().getTypeChooser().resolveType(binExp, current)); BinaryExpressionWriter bew = binExpWriter[operationType]; if (bew.writeDivision(true)) { leftExp.visit(acg); os.doGroovyCast(bew.getDevisionOpResultType()); rightExp.visit(acg); os.doGroovyCast(bew.getDevisionOpResultType()); bew.writeDivision(false); } else { super.evaluateBinaryExpression(message, binExp); } } else { int operationType = getOperandConversionType(leftType, rightType); BinaryExpressionWriter bew = binExpWriter[operationType]; if (isShiftOperation(operation) && isIntCastableType(rightExp) && bew.write(operation, true)) { leftExp.visit(acg); os.doGroovyCast(bew.getNormalOpResultType()); rightExp.visit(acg); os.doGroovyCast(int_TYPE); bew.write(operation, false); } else if (bew.write(operation, true)) { leftExp.visit(acg); os.doGroovyCast(bew.getNormalOpResultType()); rightExp.visit(acg); os.doGroovyCast(bew.getNormalOpResultType()); bew.write(operation, false); } else { super.evaluateBinaryExpression(message, binExp); } } }