protected void evaluateCompareExpression( MethodCaller compareMethod, BinaryExpression expression) { Expression leftExp = expression.getLeftExpression(); TypeChooser typeChooser = controller.getTypeChooser(); ClassNode cn = controller.getClassNode(); ClassNode leftType = typeChooser.resolveType(leftExp, cn); Expression rightExp = expression.getRightExpression(); ClassNode rightType = typeChooser.resolveType(rightExp, cn); boolean done = false; if (ClassHelper.isPrimitiveType(leftType) && ClassHelper.isPrimitiveType(rightType)) { BinaryExpressionMultiTypeDispatcher helper = new BinaryExpressionMultiTypeDispatcher(getController()); done = helper.doPrimitiveCompare(leftType, rightType, expression); } if (!done) { AsmClassGenerator acg = controller.getAcg(); OperandStack operandStack = controller.getOperandStack(); leftExp.visit(acg); operandStack.box(); rightExp.visit(acg); operandStack.box(); compareMethod.call(controller.getMethodVisitor()); ClassNode resType = ClassHelper.boolean_TYPE; if (compareMethod == findRegexMethod) { resType = ClassHelper.OBJECT_TYPE; } operandStack.replace(resType, 2); } }
private VariableSlotLoader loadWithSubscript(Expression expression) { final OperandStack operandStack = controller.getOperandStack(); // if we have a BinaryExpression, let us check if it is with // subscription if (expression instanceof BinaryExpression) { BinaryExpression be = (BinaryExpression) expression; if (be.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) { // right expression is the subscript expression // we store the result of the subscription on the stack Expression subscript = be.getRightExpression(); subscript.visit(controller.getAcg()); ClassNode subscriptType = operandStack.getTopOperand(); int id = controller.getCompileStack().defineTemporaryVariable("$subscript", subscriptType, true); VariableSlotLoader subscriptExpression = new VariableSlotLoader(subscriptType, id, operandStack); // do modified visit BinaryExpression newBe = new BinaryExpression(be.getLeftExpression(), be.getOperation(), subscriptExpression); newBe.copyNodeMetaData(be); newBe.setSourcePosition(be); newBe.visit(controller.getAcg()); return subscriptExpression; } } // normal loading of expression expression.visit(controller.getAcg()); return null; }
private void writeMapDotProperty( final Expression receiver, final String methodName, final MethodVisitor mv, final boolean safe) { receiver.visit(controller.getAcg()); // load receiver Label exit = new Label(); if (safe) { Label doGet = new Label(); mv.visitJumpInsn(IFNONNULL, doGet); controller.getOperandStack().remove(1); mv.visitInsn(ACONST_NULL); mv.visitJumpInsn(GOTO, exit); mv.visitLabel(doGet); receiver.visit(controller.getAcg()); } mv.visitLdcInsn(methodName); // load property name mv.visitMethodInsn( INVOKEINTERFACE, "java/util/Map", "get", "(Ljava/lang/Object;)Ljava/lang/Object;", true); if (safe) { mv.visitLabel(exit); } controller.getOperandStack().replace(OBJECT_TYPE); }
@Override public void visitBinaryExpression(final BinaryExpression expression) { boolean assignment = StaticTypeCheckingSupport.isAssignment(expression.getOperation().getType()); boolean isDeclaration = expression instanceof DeclarationExpression; Expression leftExpression = expression.getLeftExpression(); Expression rightExpression = expression.getRightExpression(); if (isDeclaration && leftExpression instanceof VariableExpression) { VariableExpression var = (VariableExpression) leftExpression; if (Modifier.isFinal(var.getModifiers())) { declaredFinalVariables.add(var); } } leftExpression.visit(this); inAssignment = assignment; rightExpression.visit(this); inAssignment = false; if (assignment) { if (leftExpression instanceof Variable) { boolean uninitialized = isDeclaration && rightExpression == EmptyExpression.INSTANCE; recordAssignment( (Variable) leftExpression, isDeclaration, uninitialized, false, expression); } } }
@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); }
protected void visitListOfExpressions(List<? extends Expression> list) { if (list == null) return; for (Expression expression : list) { if (expression instanceof SpreadExpression) { Expression spread = ((SpreadExpression) expression).getExpression(); spread.visit(this); } else { expression.visit(this); } } }
protected void visitListOfExpressions(List<? extends Expression> list) { if (list == null) return; for (Expression expression : list) { if (expression instanceof SpreadExpression) { Expression spread = ((SpreadExpression) expression).getExpression(); spread.visit(this); } else { // GRECLIPSE: start: could be null for malformed code (GRE290) if (expression != null) // end expression.visit(this); } } }
private void evaluateCompareTo(BinaryExpression expression) { Expression leftExpression = expression.getLeftExpression(); AsmClassGenerator acg = controller.getAcg(); OperandStack operandStack = controller.getOperandStack(); leftExpression.visit(acg); operandStack.box(); // if the right hand side is a boolean expression, we need to autobox Expression rightExpression = expression.getRightExpression(); rightExpression.visit(acg); operandStack.box(); compareToMethod.call(controller.getMethodVisitor()); operandStack.replace(ClassHelper.Integer_TYPE, 2); }
public void visitConstructorCallExpression(ConstructorCallExpression call) { isSpecialConstructorCall = call.isSpecialCall(); super.visitConstructorCallExpression(call); isSpecialConstructorCall = false; if (!call.isUsingAnonymousInnerClass()) return; pushState(); InnerClassNode innerClass = (InnerClassNode) call.getType(); innerClass.setVariableScope(currentScope); for (MethodNode method : innerClass.getMethods()) { Parameter[] parameters = method.getParameters(); if (parameters.length == 0) parameters = null; // null means no implicit "it" ClosureExpression cl = new ClosureExpression(parameters, method.getCode()); visitClosureExpression(cl); } for (FieldNode field : innerClass.getFields()) { final Expression expression = field.getInitialExpression(); if (expression != null) { expression.visit(this); } } for (Statement statement : innerClass.getObjectInitializerStatements()) { statement.visit(this); } markClosureSharedVariables(); popState(); }
@Override public void visitConstantExpression(ConstantExpression expression) { // check for inlined constant (see ResolveVisitor.transformInlineConstants) Expression original = expression.getNodeMetaData(ORIGINAL_EXPRESSION); if (original != null) { original.visit(this); } }
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); }
public void visitProperty(PropertyNode node) { visitAnnotations(node); Statement statement = node.getGetterBlock(); visitClassCodeContainer(statement); statement = node.getSetterBlock(); visitClassCodeContainer(statement); Expression init = node.getInitialExpression(); if (init != null) init.visit(this); }
public void visitAnnotations(AnnotatedNode node) { List<AnnotationNode> annotations = node.getAnnotations(); if (annotations.isEmpty()) return; for (AnnotationNode an : annotations) { // skip built-in properties if (an.isBuiltIn()) continue; for (Map.Entry<String, Expression> member : an.getMembers().entrySet()) { Expression annMemberValue = member.getValue(); annMemberValue.visit(this); } } }
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); }
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; }
protected void assignToArray( Expression parent, Expression receiver, Expression index, Expression rhsValueLoader) { // let's replace this assignment to a subscript operator with a // method call // e.g. x[5] = 10 // -> (x, [], 5), =, 10 // -> methodCall(x, "putAt", [5, 10]) ArgumentListExpression ae = new ArgumentListExpression(index, rhsValueLoader); controller .getInvocationWriter() .makeCall( parent, receiver, new ConstantExpression("putAt"), ae, InvocationWriter.invokeMethod, false, false, false); controller.getOperandStack().pop(); // return value of assignment rhsValueLoader.visit(controller.getAcg()); }
protected void evaluateBinaryExpressionWithAssignment( String method, BinaryExpression expression) { Expression leftExpression = expression.getLeftExpression(); AsmClassGenerator acg = controller.getAcg(); OperandStack operandStack = controller.getOperandStack(); if (leftExpression instanceof BinaryExpression) { BinaryExpression leftBinExpr = (BinaryExpression) leftExpression; if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) { evaluateArrayAssignmentWithOperator(method, expression, leftBinExpr); return; } } evaluateBinaryExpression(method, expression); // br to leave a copy of rvalue on the stack. see also isPopRequired() operandStack.dup(); controller.getCompileStack().pushLHS(true); leftExpression.visit(acg); controller.getCompileStack().popLHS(); }
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 }
@Override public void makeGetPropertySite( Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) { Object dynamic = receiver.getNodeMetaData(StaticCompilationMetadataKeys.RECEIVER_OF_DYNAMIC_PROPERTY); if (dynamic != null) { MethodNode target = safe ? INVOKERHELPER_GETPROPERTYSAFE_METHOD : INVOKERHELPER_GETPROPERTY_METHOD; MethodCallExpression mce = new MethodCallExpression( new ClassExpression(INVOKERHELPER_TYPE), target.getName(), new ArgumentListExpression(receiver, new ConstantExpression(methodName))); mce.setSafe(false); mce.setImplicitThis(false); mce.setMethodTarget(target); mce.visit(controller.getAcg()); return; } TypeChooser typeChooser = controller.getTypeChooser(); ClassNode classNode = controller.getClassNode(); ClassNode receiverType = (ClassNode) receiver.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER); if (receiverType == null) { receiverType = typeChooser.resolveType(receiver, classNode); } Object type = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); if (type == null && receiver instanceof VariableExpression) { Variable variable = ((VariableExpression) receiver).getAccessedVariable(); if (variable instanceof Expression) { type = ((Expression) variable).getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); } } if (type != null) { // in case a "flow type" is found, it is preferred to use it instead of // the declaration type receiverType = (ClassNode) type; } boolean isClassReceiver = false; if (isClassClassNodeWrappingConcreteType(receiverType)) { isClassReceiver = true; receiverType = receiverType.getGenericsTypes()[0].getType(); } MethodVisitor mv = controller.getMethodVisitor(); if (receiverType.isArray() && methodName.equals("length")) { receiver.visit(controller.getAcg()); ClassNode arrayGetReturnType = typeChooser.resolveType(receiver, classNode); controller.getOperandStack().doGroovyCast(arrayGetReturnType); mv.visitInsn(ARRAYLENGTH); controller.getOperandStack().replace(int_TYPE); return; } else if ((receiverType.implementsInterface(COLLECTION_TYPE) || COLLECTION_TYPE.equals(receiverType)) && ("size".equals(methodName) || "length".equals(methodName))) { MethodCallExpression expr = new MethodCallExpression(receiver, "size", ArgumentListExpression.EMPTY_ARGUMENTS); expr.setMethodTarget(COLLECTION_SIZE_METHOD); expr.setImplicitThis(implicitThis); expr.setSafe(safe); expr.visit(controller.getAcg()); return; } if (makeGetPropertyWithGetter(receiver, receiverType, methodName, safe, implicitThis)) return; if (makeGetField( receiver, receiverType, methodName, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return; if (receiverType.isEnum()) { mv.visitFieldInsn( GETSTATIC, BytecodeHelper.getClassInternalName(receiverType), methodName, BytecodeHelper.getTypeDescription(receiverType)); controller.getOperandStack().push(receiverType); return; } if (receiver instanceof ClassExpression) { if (makeGetField( receiver, receiver.getType(), methodName, implicitThis, samePackages(receiver.getType().getPackageName(), classNode.getPackageName()))) return; if (makeGetPropertyWithGetter(receiver, receiver.getType(), methodName, safe, implicitThis)) return; if (makeGetPrivateFieldWithBridgeMethod( receiver, receiver.getType(), methodName, safe, implicitThis)) return; } if (isClassReceiver) { // we are probably looking for a property of the class if (makeGetPropertyWithGetter(receiver, CLASS_Type, methodName, safe, implicitThis)) return; if (makeGetField(receiver, CLASS_Type, methodName, false, true)) return; } if (makeGetPrivateFieldWithBridgeMethod(receiver, receiverType, methodName, safe, implicitThis)) return; // GROOVY-5580, it is still possible that we're calling a superinterface property String getterName = "get" + MetaClassHelper.capitalize(methodName); if (receiverType.isInterface()) { Set<ClassNode> allInterfaces = receiverType.getAllInterfaces(); MethodNode getterMethod = null; for (ClassNode anInterface : allInterfaces) { getterMethod = anInterface.getGetterMethod(getterName); if (getterMethod != null) break; } // GROOVY-5585 if (getterMethod == null) { getterMethod = OBJECT_TYPE.getGetterMethod(getterName); } if (getterMethod != null) { MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setMethodTarget(getterMethod); call.setImplicitThis(false); call.setSourcePosition(receiver); call.visit(controller.getAcg()); return; } } // GROOVY-5568, we would be facing a DGM call, but instead of foo.getText(), have foo.text List<MethodNode> methods = findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), receiverType, getterName, ClassNode.EMPTY_ARRAY); if (!methods.isEmpty()) { List<MethodNode> methodNodes = chooseBestMethod(receiverType, methods, ClassNode.EMPTY_ARRAY); if (methodNodes.size() == 1) { MethodNode getter = methodNodes.get(0); MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setMethodTarget(getter); call.setImplicitThis(false); call.setSourcePosition(receiver); call.visit(controller.getAcg()); return; } } boolean isStaticProperty = receiver instanceof ClassExpression && (receiverType.isDerivedFrom(receiver.getType()) || receiverType.implementsInterface(receiver.getType())); if (!isStaticProperty) { if (receiverType.implementsInterface(MAP_TYPE) || MAP_TYPE.equals(receiverType)) { // for maps, replace map.foo with map.get('foo') writeMapDotProperty(receiver, methodName, mv, safe); return; } if (receiverType.implementsInterface(LIST_TYPE) || LIST_TYPE.equals(receiverType)) { writeListDotProperty(receiver, methodName, mv, safe); return; } } controller .getSourceUnit() .addError( new SyntaxException( "Access to " + (receiver instanceof ClassExpression ? receiver.getType() : receiverType) .toString(false) + "#" + methodName + " is forbidden", receiver.getLineNumber(), receiver.getColumnNumber(), receiver.getLastLineNumber(), receiver.getLastColumnNumber())); controller.getMethodVisitor().visitInsn(ACONST_NULL); controller.getOperandStack().push(OBJECT_TYPE); }
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 public void makeGroovyObjectGetPropertySite( final Expression receiver, final String methodName, final boolean safe, final boolean implicitThis) { TypeChooser typeChooser = controller.getTypeChooser(); ClassNode classNode = controller.getClassNode(); ClassNode receiverType = typeChooser.resolveType(receiver, classNode); if (receiver instanceof VariableExpression && ((VariableExpression) receiver).isThisExpression() && !controller.isInClosure()) { receiverType = classNode; } String property = methodName; if (implicitThis) { if (controller.getInvocationWriter() instanceof StaticInvocationWriter) { MethodCallExpression currentCall = ((StaticInvocationWriter) controller.getInvocationWriter()).getCurrentCall(); if (currentCall != null && currentCall.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER) != null) { property = (String) currentCall.getNodeMetaData(StaticTypesMarker.IMPLICIT_RECEIVER); String[] props = property.split("\\."); BytecodeExpression thisLoader = new BytecodeExpression() { @Override public void visit(final MethodVisitor mv) { mv.visitVarInsn(ALOAD, 0); // load this } }; thisLoader.setType(CLOSURE_TYPE); Expression pexp = new PropertyExpression(thisLoader, new ConstantExpression(props[0]), safe); for (int i = 1, propsLength = props.length; i < propsLength; i++) { final String prop = props[i]; pexp.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, CLOSURE_TYPE); pexp = new PropertyExpression(pexp, prop); } pexp.visit(controller.getAcg()); return; } } } if (makeGetPropertyWithGetter(receiver, receiverType, property, safe, implicitThis)) return; if (makeGetField( receiver, receiverType, property, implicitThis, samePackages(receiverType.getPackageName(), classNode.getPackageName()))) return; MethodCallExpression call = new MethodCallExpression( receiver, "getProperty", new ArgumentListExpression(new ConstantExpression(property))); call.setImplicitThis(implicitThis); call.setSafe(safe); call.setMethodTarget(GROOVYOBJECT_GETPROPERTY_METHOD); call.visit(controller.getAcg()); return; }
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); }
public void visitField(FieldNode node) { visitAnnotations(node); Expression init = node.getInitialExpression(); if (init != null) init.visit(this); }
protected void visitAnnotation(AnnotationNode node) { for (Expression expr : node.getMembers().values()) { expr.visit(this); } }
@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); } } }