private String[] ensureInitialized(ClassNode domainClass) { FieldNode field = domainClass.getField("namedQueries"); if (field != null && field.isStatic()) { Expression initialExpression = field.getInitialExpression(); if (initialExpression instanceof ClosureExpression) { Statement code = ((ClosureExpression) initialExpression).getCode(); if (code instanceof BlockStatement) { List<Statement> statements = ((BlockStatement) code).getStatements(); if (statements != null) { List<String> namedQueries = new ArrayList<String>(statements.size()); for (Statement s : statements) { if (s instanceof ExpressionStatement) { Expression expr = ((ExpressionStatement) s).getExpression(); if (expr instanceof MethodCallExpression) { MethodCallExpression call = (MethodCallExpression) expr; namedQueries.add(call.getMethodAsString()); } } } return namedQueries.toArray(NO_QUERIES); } } } } return NO_QUERIES; }
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()); }
private Expression transformMethodCallExpression(final MethodCallExpression exp) { Expression objectExpression = transform(exp.getObjectExpression()); ClassNode traitReceiver = objectExpression.getNodeMetaData(SuperCallTraitTransformer.class); if (traitReceiver != null) { TraitHelpersTuple helpers = Traits.findHelpers(traitReceiver); // (SomeTrait.super).foo() --> SomeTrait$Helper.foo(this) ClassExpression receiver = new ClassExpression(helpers.getHelper()); ArgumentListExpression newArgs = new ArgumentListExpression(); Expression arguments = exp.getArguments(); newArgs.addExpression(new VariableExpression("this")); if (arguments instanceof TupleExpression) { List<Expression> expressions = ((TupleExpression) arguments).getExpressions(); for (Expression expression : expressions) { newArgs.addExpression(transform(expression)); } } else { newArgs.addExpression(arguments); } MethodCallExpression result = new MethodCallExpression(receiver, exp.getMethod(), newArgs); result.setImplicitThis(false); result.setSpreadSafe(exp.isSpreadSafe()); result.setSafe(exp.isSafe()); result.setSourcePosition(exp); return result; } return super.transform(exp); }
public Expression transform(UnaryMinusExpression exp, CompilerTransformer compiler) { final BytecodeExpr inner = (BytecodeExpr) compiler.transform(exp.getExpression()); final ClassNode type = ClassHelper.getUnwrapper(inner.getType()); if (type == ClassHelper.byte_TYPE || type == ClassHelper.short_TYPE || type == ClassHelper.int_TYPE || type == ClassHelper.long_TYPE || type == ClassHelper.float_TYPE || type == ClassHelper.double_TYPE) { return new BytecodeExpr(exp, type) { protected void compile(MethodVisitor mv) { inner.visit(mv); if (!ClassHelper.isPrimitiveType(inner.getType())) unbox(type, mv); if (type == ClassHelper.int_TYPE || type == ClassHelper.short_TYPE || type == ClassHelper.byte_TYPE) mv.visitInsn(INEG); else if (type == ClassHelper.long_TYPE) mv.visitInsn(LNEG); else if (type == ClassHelper.float_TYPE) mv.visitInsn(FNEG); else if (type == ClassHelper.double_TYPE) mv.visitInsn(DNEG); } }; } else { MethodCallExpression negate = new MethodCallExpression(inner, "negate", new ArgumentListExpression()); negate.setSourcePosition(exp); return compiler.transform(negate); } }
/** * Evaluates a constraints closure and returns metadata about the constraints configured in the * closure. The Map returned has property names as keys and the value associated with each of * those property names is a Map<String, Expression> which has constraint names as keys and the * Expression associated with that constraint as values * * @param closureExpression the closure expression to evaluate * @return the Map as described above */ public static Map<String, Map<String, Expression>> getConstraintMetadata( final ClosureExpression closureExpression) { final List<MethodCallExpression> methodExpressions = new ArrayList<MethodCallExpression>(); final Map<String, Map<String, Expression>> results = new LinkedHashMap<String, Map<String, Expression>>(); final Statement closureCode = closureExpression.getCode(); if (closureCode instanceof BlockStatement) { final List<Statement> closureStatements = ((BlockStatement) closureCode).getStatements(); for (final Statement closureStatement : closureStatements) { if (closureStatement instanceof ExpressionStatement) { final Expression expression = ((ExpressionStatement) closureStatement).getExpression(); if (expression instanceof MethodCallExpression) { methodExpressions.add((MethodCallExpression) expression); } } else if (closureStatement instanceof ReturnStatement) { final ReturnStatement returnStatement = (ReturnStatement) closureStatement; Expression expression = returnStatement.getExpression(); if (expression instanceof MethodCallExpression) { methodExpressions.add((MethodCallExpression) expression); } } for (final MethodCallExpression methodCallExpression : methodExpressions) { final Expression objectExpression = methodCallExpression.getObjectExpression(); if (objectExpression instanceof VariableExpression && "this".equals(((VariableExpression) objectExpression).getName())) { final Expression methodCallArguments = methodCallExpression.getArguments(); if (methodCallArguments instanceof TupleExpression) { final List<Expression> methodCallArgumentExpressions = ((TupleExpression) methodCallArguments).getExpressions(); if (methodCallArgumentExpressions != null && methodCallArgumentExpressions.size() == 1 && methodCallArgumentExpressions.get(0) instanceof NamedArgumentListExpression) { final Map<String, Expression> constraintNameToExpression = new LinkedHashMap<String, Expression>(); final List<MapEntryExpression> mapEntryExpressions = ((NamedArgumentListExpression) methodCallArgumentExpressions.get(0)) .getMapEntryExpressions(); for (final MapEntryExpression mapEntryExpression : mapEntryExpressions) { final Expression keyExpression = mapEntryExpression.getKeyExpression(); if (keyExpression instanceof ConstantExpression) { final Object value = ((ConstantExpression) keyExpression).getValue(); if (value instanceof String) { constraintNameToExpression.put( (String) value, mapEntryExpression.getValueExpression()); } } } results.put(methodCallExpression.getMethodAsString(), constraintNameToExpression); } } } } } } return results; }
@Override public void visitMethodCallExpression(final MethodCallExpression call) { super.visitMethodCallExpression(call); MethodNode mn = call.getMethodTarget(); if (mn == null) { call.setMethodTarget(doCallMethod); } }
public static Expression createArgumentList(String source) { AstBuilder b = new AstBuilder(); String call = "m(" + source + ")"; List<ASTNode> astNodes = b.buildFromString(CompilePhase.CONVERSION, true, call); BlockStatement block = (BlockStatement) astNodes.get(0); MethodCallExpression mce = (MethodCallExpression) ((ExpressionStatement) block.getStatements().get(0)).getExpression(); ArgumentListExpression args = (ArgumentListExpression) mce.getArguments(); return args; }
/** * This method is used to add "bridge" methods for private methods of an inner/outer class, so * that the outer class is capable of calling them. It does basically the same job as access$000 * like methods in Java. * * @param node an inner/outer class node for which to generate bridge methods */ @SuppressWarnings("unchecked") private void addPrivateBridgeMethods(final ClassNode node) { Set<ASTNode> accessedMethods = (Set<ASTNode>) node.getNodeMetaData(StaticTypesMarker.PV_METHODS_ACCESS); if (accessedMethods == null) return; List<MethodNode> methods = new ArrayList<MethodNode>(node.getAllDeclaredMethods()); Map<MethodNode, MethodNode> privateBridgeMethods = (Map<MethodNode, MethodNode>) node.getNodeMetaData(PRIVATE_BRIDGE_METHODS); if (privateBridgeMethods != null) { // private bridge methods already added return; } privateBridgeMethods = new HashMap<MethodNode, MethodNode>(); int i = -1; final int access = Opcodes.ACC_STATIC | Opcodes.ACC_PUBLIC | Opcodes.ACC_SYNTHETIC; for (MethodNode method : methods) { if (accessedMethods.contains(method)) { i++; Parameter[] methodParameters = method.getParameters(); Parameter[] newParams = new Parameter[methodParameters.length + 1]; System.arraycopy(methodParameters, 0, newParams, 1, methodParameters.length); newParams[0] = new Parameter(node.getPlainNodeReference(), "$that"); Expression arguments; if (method.getParameters() == null || method.getParameters().length == 0) { arguments = ArgumentListExpression.EMPTY_ARGUMENTS; } else { List<Expression> args = new LinkedList<Expression>(); for (Parameter parameter : methodParameters) { args.add(new VariableExpression(parameter)); } arguments = new ArgumentListExpression(args); } Expression receiver = method.isStatic() ? new ClassExpression(node) : new VariableExpression(newParams[0]); MethodCallExpression mce = new MethodCallExpression(receiver, method.getName(), arguments); mce.setMethodTarget(method); ExpressionStatement returnStatement = new ExpressionStatement(mce); MethodNode bridge = node.addMethod( "access$" + i, access, method.getReturnType(), newParams, method.getExceptions(), returnStatement); privateBridgeMethods.put(method, bridge); bridge.addAnnotation(new AnnotationNode(COMPILESTATIC_CLASSNODE)); } } if (!privateBridgeMethods.isEmpty()) { node.setNodeMetaData(PRIVATE_BRIDGE_METHODS, privateBridgeMethods); } }
/** * Adds a delegate method to the target class node where the first argument is to the delegate * method is 'this'. In other words a method such as foo(Object instance, String bar) would be * added with a signature of foo(String) and 'this' is passed to the delegate instance * * @param classNode The class node * @param delegate The expression that looks up the delegate * @param declaredMethod The declared method * @param thisAsFirstArgument Whether 'this' should be passed as the first argument to the method * @return The added method node or null if it couldn't be added */ public static MethodNode addDelegateInstanceMethod( ClassNode classNode, Expression delegate, MethodNode declaredMethod, boolean thisAsFirstArgument) { Parameter[] parameterTypes = thisAsFirstArgument ? getRemainingParameterTypes(declaredMethod.getParameters()) : declaredMethod.getParameters(); String methodName = declaredMethod.getName(); if (classNode.hasDeclaredMethod(methodName, parameterTypes)) { return null; } String propertyName = GrailsClassUtils.getPropertyForGetter(methodName); if (propertyName != null && parameterTypes.length == 0 && classNode.hasProperty(propertyName)) { return null; } propertyName = GrailsClassUtils.getPropertyForSetter(methodName); if (propertyName != null && parameterTypes.length == 1 && classNode.hasProperty(propertyName)) { return null; } BlockStatement methodBody = new BlockStatement(); ArgumentListExpression arguments = createArgumentListFromParameters(parameterTypes, thisAsFirstArgument); ClassNode returnType = nonGeneric(declaredMethod.getReturnType()); MethodCallExpression methodCallExpression = new MethodCallExpression(delegate, methodName, arguments); methodCallExpression.setMethodTarget(declaredMethod); ThrowStatement missingMethodException = createMissingMethodThrowable(classNode, declaredMethod); VariableExpression apiVar = addApiVariableDeclaration(delegate, declaredMethod, methodBody); IfStatement ifStatement = createIfElseStatementForApiMethodCall(methodCallExpression, apiVar, missingMethodException); methodBody.addStatement(ifStatement); MethodNode methodNode = new MethodNode( methodName, Modifier.PUBLIC, returnType, copyParameters(parameterTypes), GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY, methodBody); methodNode.addAnnotations(declaredMethod.getAnnotations()); classNode.addMethod(methodNode); return methodNode; }
public static BytecodeExpr transformLogicalExpression( Expression exp, CompilerTransformer compiler, Label label, boolean onTrue) { if (exp instanceof StaticMethodCallExpression) { StaticMethodCallExpression smce = (StaticMethodCallExpression) exp; MethodCallExpression mce = new MethodCallExpression( new ClassExpression(smce.getOwnerType()), smce.getMethod(), smce.getArguments()); mce.setSourcePosition(smce); return transformLogicalExpression(mce, compiler, label, onTrue); } ExprTransformer t = transformers.get(exp.getClass()); return t.transformLogical(exp, compiler, label, onTrue); }
@Override public void visitMethodCallExpression(final MethodCallExpression call) { super.visitMethodCallExpression(call); MethodNode target = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); if (target != null) { call.setMethodTarget(target); memorizeInitialExpressions(target); } if (call.getMethodTarget() == null && call.getLineNumber() > 0) { addError("Target method for method call expression hasn't been set", call); } }
@Override public List<MethodNode> handleMissingMethod( final ClassNode receiver, final String name, final ArgumentListExpression argumentList, final ClassNode[] argumentTypes, final MethodCall call) { String[] decomposed = Traits.decomposeSuperCallName(name); if (decomposed != null) { return convertToDynamicCall(call, receiver, decomposed, argumentTypes); } if (call instanceof MethodCallExpression) { MethodCallExpression mce = (MethodCallExpression) call; if (mce.getReceiver() instanceof VariableExpression) { VariableExpression var = (VariableExpression) mce.getReceiver(); // GROOVY-7322 // static method call in trait? ClassNode type = null; if (isStaticTraitReceiver(receiver, var)) { type = receiver.getGenericsTypes()[0].getType(); } else if (isThisTraitReceiver(var)) { type = receiver; } if (type != null && Traits.isTrait(type)) { ClassNode helper = Traits.findHelper(type); Parameter[] params = new Parameter[argumentTypes.length + 1]; params[0] = new Parameter(ClassHelper.CLASS_Type.getPlainNodeReference(), "staticSelf"); for (int i = 1; i < params.length; i++) { params[i] = new Parameter(argumentTypes[i - 1], "p" + i); } MethodNode method = helper.getDeclaredMethod(name, params); if (method != null) { return Collections.singletonList(makeDynamic(call, method.getReturnType())); } } } ClassNode dynamic = mce.getNodeMetaData(TraitASTTransformation.DO_DYNAMIC); if (dynamic != null) { return Collections.singletonList(makeDynamic(call, dynamic)); } } return NOTFOUND; }
/** * Adds a static method to the given class node that delegates to the given method and resolves * the object to invoke the method on from the given expression. * * @param expression The expression * @param classNode The class node * @param delegateMethod The delegate method * @return The added method node or null if it couldn't be added */ public static MethodNode addDelegateStaticMethod( Expression expression, ClassNode classNode, MethodNode delegateMethod) { Parameter[] parameterTypes = delegateMethod.getParameters(); String declaredMethodName = delegateMethod.getName(); if (classNode.hasDeclaredMethod(declaredMethodName, parameterTypes)) { return null; } BlockStatement methodBody = new BlockStatement(); ArgumentListExpression arguments = new ArgumentListExpression(); for (Parameter parameterType : parameterTypes) { arguments.addExpression(new VariableExpression(parameterType.getName())); } MethodCallExpression methodCallExpression = new MethodCallExpression(expression, declaredMethodName, arguments); methodCallExpression.setMethodTarget(delegateMethod); ThrowStatement missingMethodException = createMissingMethodThrowable(classNode, delegateMethod); VariableExpression apiVar = addApiVariableDeclaration(expression, delegateMethod, methodBody); IfStatement ifStatement = createIfElseStatementForApiMethodCall(methodCallExpression, apiVar, missingMethodException); methodBody.addStatement(ifStatement); ClassNode returnType = nonGeneric(delegateMethod.getReturnType()); if (METHOD_MISSING_METHOD_NAME.equals(declaredMethodName)) { declaredMethodName = STATIC_METHOD_MISSING_METHOD_NAME; } MethodNode methodNode = classNode.getDeclaredMethod(declaredMethodName, parameterTypes); if (methodNode == null) { methodNode = new MethodNode( declaredMethodName, Modifier.PUBLIC | Modifier.STATIC, returnType, copyParameters(parameterTypes), GrailsArtefactClassInjector.EMPTY_CLASS_ARRAY, methodBody); methodNode.addAnnotations(delegateMethod.getAnnotations()); classNode.addMethod(methodNode); } return methodNode; }
public List<IGroovyProposal> extraProposals( ClassNode declaringType, ResolverCache resolver, Expression expression) { // first find the arguments that are possible Map<String, ClassNode> availableParams = findAvailableParamNames(resolver); if (availableParams.isEmpty()) { return ProposalUtils.NO_PROPOSALS; } if (expression instanceof MethodCallExpression) { // next find out if there are any existing named args MethodCallExpression call = (MethodCallExpression) expression; Expression arguments = call.getArguments(); if (arguments instanceof TupleExpression) { for (Expression maybeArg : ((TupleExpression) arguments).getExpressions()) { if (maybeArg instanceof MapExpression) { arguments = maybeArg; break; } } } // now remove the arguments that are already written if (arguments instanceof MapExpression) { // Do extra filtering to determine what parameters are still available MapExpression enclosingCallArgs = (MapExpression) arguments; for (MapEntryExpression entry : enclosingCallArgs.getMapEntryExpressions()) { String paramName = entry.getKeyExpression().getText(); availableParams.remove(paramName); } } } List<IGroovyProposal> extraProposals = new ArrayList<IGroovyProposal>(availableParams.size()); for (Entry<String, ClassNode> available : availableParams.entrySet()) { extraProposals.add( new GroovyNamedArgumentProposal( available.getKey(), available.getValue(), toMethod(declaringType.redirect(), resolver), provider)); } return extraProposals; }
private Expression convertInOperatorToTernary( final BinaryExpression bin, final Expression rightExpression, final Expression leftExpression) { MethodCallExpression call = new MethodCallExpression(rightExpression, "isCase", leftExpression); call.setMethodTarget( (MethodNode) bin.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET)); call.setSourcePosition(bin); call.copyNodeMetaData(bin); TernaryExpression tExp = new TernaryExpression( new BooleanExpression( new BinaryExpression( rightExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null))), new BinaryExpression( leftExpression, Token.newSymbol("==", -1, -1), new ConstantExpression(null)), call); return staticCompilationTransformer.transform(tExp); }
public void visitMethodCallExpression(MethodCallExpression mce) { super.visitMethodCallExpression(mce); Expression aexp = mce.getArguments(); if (aexp instanceof TupleExpression) { TupleExpression arguments = (TupleExpression) aexp; for (Expression e : arguments.getExpressions()) { checkForInvalidDeclaration(e); } } else { checkForInvalidDeclaration(aexp); } }
public void visitMethodCallExpression(MethodCallExpression call) { if (call.isImplicitThis() && call.getMethod() instanceof ConstantExpression) { ConstantExpression methodNameConstant = (ConstantExpression) call.getMethod(); Object value = methodNameConstant.getText(); if (!(value instanceof String)) { throw new GroovyBugError( "tried to make a method call with a non-String constant method name."); } String methodName = (String) value; Variable v = checkVariableNameForDeclaration(methodName, call); if (v != null && !(v instanceof DynamicVariable)) { checkVariableContextAccess(v, call); } if (v instanceof VariableExpression || v instanceof Parameter) { VariableExpression object = new VariableExpression(v); object.setSourcePosition(methodNameConstant); call.setObjectExpression(object); ConstantExpression method = new ConstantExpression("call"); method.setSourcePosition(methodNameConstant); // important for GROOVY-4344 call.setImplicitThis(false); call.setMethod(method); } } super.visitMethodCallExpression(call); }
@Override public void visitMethodCallExpression(MethodCallExpression call) { String name = call.getMethodAsString(); // Could be null (for 'funny' calls where target name is dynamic) SearchingCodeVisitor.MethodCallAction action = getMethodCallAction(name); if (action != null) { Assert.isLegal(action.methodName.equals(name)); if (!isVisited(call)) { action.doit(this, call); } } super.visitMethodCallExpression(call); }
@SuppressWarnings("unchecked") private boolean makeGetPrivateFieldWithBridgeMethod( final Expression receiver, final ClassNode receiverType, final String fieldName, final boolean safe, final boolean implicitThis) { FieldNode field = receiverType.getField(fieldName); ClassNode classNode = controller.getClassNode(); if (field != null && Modifier.isPrivate(field.getModifiers()) && (StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(receiverType, classNode) || StaticInvocationWriter.isPrivateBridgeMethodsCallAllowed(classNode, receiverType)) && !receiverType.equals(classNode)) { Map<String, MethodNode> accessors = (Map<String, MethodNode>) receiverType .redirect() .getNodeMetaData(StaticCompilationMetadataKeys.PRIVATE_FIELDS_ACCESSORS); if (accessors != null) { MethodNode methodNode = accessors.get(fieldName); if (methodNode != null) { MethodCallExpression mce = new MethodCallExpression( receiver, methodNode.getName(), new ArgumentListExpression( field.isStatic() ? new ConstantExpression(null) : receiver)); mce.setMethodTarget(methodNode); mce.setSafe(safe); mce.setImplicitThis(implicitThis); mce.visit(controller.getAcg()); return true; } } } return false; }
@Override public void visitMethodCallExpression(MethodCallExpression expression) { // LOG.debug "Transforming expression '${expression}':" if (expression.getLineNumber() >= 0 && expression.getLineNumber() < lineNumbers.length) { // LOG.debug " start from ${expression.lineNumber} to ${lineNumbers[expression.lineNumber // - 1]}" expression.setLineNumber(lineNumbers[expression.getLineNumber() - 1]); } if (expression.getLastLineNumber() > 0 && expression.getLastLineNumber() < lineNumbers.length) { // LOG.debug " end from ${expression.lastLineNumber} to // ${lineNumbers[expression.lastLineNumber - 1]}" expression.setLastLineNumber(lineNumbers[expression.getLastLineNumber() - 1]); } super.visitMethodCallExpression(expression); }
/** * Add a new Violation to the list of violations found by this visitor. Only add the violation if * the node lineNumber >= 0. * * @param node - the Groovy AST Node * @param message - the message for the violation; defaults to null */ protected void addViolation(MethodCallExpression node, String message) { if (node.getLineNumber() >= 0) { int lineNumber = AstUtil.findFirstNonAnnotationLine(node, sourceCode); String sourceLine = sourceCode.line(AstUtil.findFirstNonAnnotationLine(node, sourceCode) - 1); Violation violation = new Violation(); violation.setRule(rule); violation.setLineNumber(lineNumber); violation.setSourceLine(sourceLine); if (currentClassNode != null) { violation.setMessage( String.format("Violation in class %s. %s", currentClassNode.getName(), message)); } else { violation.setMessage(message); } violations.add(violation); } }
private void createDirectCallMethod(final ClassNode closureClass, final MethodNode doCallMethod) { // in case there is no "call" method on the closure, we can create a "fast invocation" paths // to avoid going through ClosureMetaClass by call(Object...) method // we can't have a specialized version of call(Object...) because the dispatch logic in // ClosureMetaClass // is too complex! // call(Object) Parameter args = new Parameter(ClassHelper.OBJECT_TYPE, "args"); MethodCallExpression doCall1arg = new MethodCallExpression( new VariableExpression("this"), "doCall", new ArgumentListExpression(new VariableExpression(args))); doCall1arg.setImplicitThis(true); doCall1arg.setMethodTarget(doCallMethod); closureClass.addMethod( new MethodNode( "call", Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE, new Parameter[] {args}, ClassNode.EMPTY_ARRAY, new ReturnStatement(doCall1arg))); // call() MethodCallExpression doCallNoArgs = new MethodCallExpression( new VariableExpression("this"), "doCall", new ArgumentListExpression(new ConstantExpression(null))); doCallNoArgs.setImplicitThis(true); doCallNoArgs.setMethodTarget(doCallMethod); closureClass.addMethod( new MethodNode( "call", Opcodes.ACC_PUBLIC, ClassHelper.OBJECT_TYPE, Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, new ReturnStatement(doCallNoArgs))); }
@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 trySubscript( final Expression receiver, final String message, final Expression arguments, ClassNode rType, final ClassNode aType) { if (getWrapper(rType).isDerivedFrom(Number_TYPE) && getWrapper(aType).isDerivedFrom(Number_TYPE)) { if ("plus".equals(message) || "minus".equals(message) || "multiply".equals(message) || "div".equals(message)) { writeNumberNumberCall(receiver, message, arguments); return true; } else if ("power".equals(message)) { writePowerCall(receiver, arguments, rType, aType); return true; } else if ("mod".equals(message)) { writeModCall(receiver, arguments, rType, aType); return true; } } else if (STRING_TYPE.equals(rType) && "plus".equals(message)) { writeStringPlusCall(receiver, message, arguments); return true; } else if ("getAt".equals(message)) { if (rType.isArray() && getWrapper(aType).isDerivedFrom(Number_TYPE)) { writeArrayGet(receiver, arguments, rType, aType); return true; } else { // check if a getAt method can be found on the receiver ClassNode current = rType; MethodNode getAtNode = null; while (current != null && getAtNode == null) { getAtNode = current.getMethod("getAt", new Parameter[] {new Parameter(aType, "index")}); if (getAtNode == null && isPrimitiveType(aType)) { getAtNode = current.getMethod( "getAt", new Parameter[] {new Parameter(getWrapper(aType), "index")}); } else if (getAtNode == null && aType.isDerivedFrom(Number_TYPE)) { getAtNode = current.getMethod( "getAt", new Parameter[] {new Parameter(getUnwrapper(aType), "index")}); } current = current.getSuperClass(); } if (getAtNode != null) { MethodCallExpression call = new MethodCallExpression(receiver, "getAt", arguments); call.setSourcePosition(arguments); call.setImplicitThis(false); call.setMethodTarget(getAtNode); call.visit(controller.getAcg()); return true; } // make sure Map#getAt() and List#getAt handled with the bracket syntax are properly // compiled ClassNode[] args = {aType}; boolean acceptAnyMethod = MAP_TYPE.equals(rType) || rType.implementsInterface(MAP_TYPE) || LIST_TYPE.equals(rType) || rType.implementsInterface(LIST_TYPE); List<MethodNode> nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), rType, message, args); if (nodes.isEmpty()) { // retry with raw types rType = rType.getPlainNodeReference(); nodes = StaticTypeCheckingSupport.findDGMMethodsByNameAndArguments( controller.getSourceUnit().getClassLoader(), rType, message, args); } nodes = StaticTypeCheckingSupport.chooseBestMethod(rType, nodes, args); if (nodes.size() == 1 || nodes.size() > 1 && acceptAnyMethod) { MethodNode methodNode = nodes.get(0); MethodCallExpression call = new MethodCallExpression(receiver, message, arguments); call.setSourcePosition(arguments); call.setImplicitThis(false); call.setMethodTarget(methodNode); call.visit(controller.getAcg()); return true; } if (implementsInterfaceOrIsSubclassOf(rType, MAP_TYPE)) { // fallback to Map#get MethodCallExpression call = new MethodCallExpression(receiver, "get", arguments); call.setMethodTarget(MAP_GET_METHOD); call.setSourcePosition(arguments); call.setImplicitThis(false); call.visit(controller.getAcg()); return true; } } } return false; }
private boolean makeGetPropertyWithGetter( final Expression receiver, final ClassNode receiverType, final String methodName, final boolean safe, final boolean implicitThis) { // does a getter exists ? String getterName = "get" + MetaClassHelper.capitalize(methodName); MethodNode getterNode = receiverType.getGetterMethod(getterName); if (getterNode == null) { getterName = "is" + MetaClassHelper.capitalize(methodName); getterNode = receiverType.getGetterMethod(getterName); } if (getterNode != null && receiver instanceof ClassExpression && !CLASS_Type.equals(receiverType) && !getterNode.isStatic()) { return false; } // GROOVY-5561: if two files are compiled in the same source unit // and that one references the other, the getters for properties have not been // generated by the compiler yet (generated by the Verifier) PropertyNode propertyNode = receiverType.getProperty(methodName); if (propertyNode != null) { // it is possible to use a getter String prefix = "get"; if (boolean_TYPE.equals(propertyNode.getOriginType())) { prefix = "is"; } getterName = prefix + MetaClassHelper.capitalize(methodName); getterNode = new MethodNode( getterName, ACC_PUBLIC, propertyNode.getOriginType(), Parameter.EMPTY_ARRAY, ClassNode.EMPTY_ARRAY, EmptyStatement.INSTANCE); getterNode.setDeclaringClass(receiverType); if (propertyNode.isStatic()) getterNode.setModifiers(ACC_PUBLIC + ACC_STATIC); } if (getterNode != null) { MethodCallExpression call = new MethodCallExpression(receiver, getterName, ArgumentListExpression.EMPTY_ARGUMENTS); call.setSourcePosition(receiver); call.setMethodTarget(getterNode); call.setImplicitThis(implicitThis); call.setSafe(safe); call.visit(controller.getAcg()); return true; } if (receiverType instanceof InnerClassNode && !receiverType.isStaticClass()) { if (makeGetPropertyWithGetter( receiver, receiverType.getOuterClass(), methodName, safe, implicitThis)) { return true; } } // go upper level ClassNode superClass = receiverType.getSuperClass(); if (superClass != null) { return makeGetPropertyWithGetter(receiver, superClass, methodName, safe, implicitThis); } return false; }
@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; }
protected Expression transformMethodCallExpression(MethodCallExpression mce) { Expression args = transform(mce.getArguments()); Expression method = transform(mce.getMethod()); Expression object = transform(mce.getObjectExpression()); boolean isExplicitThisOrSuper = false; boolean isExplicitSuper = false; if (object instanceof VariableExpression) { VariableExpression ve = (VariableExpression) object; isExplicitThisOrSuper = !mce.isImplicitThis() && (ve.isThisExpression() || ve.isSuperExpression()); isExplicitSuper = ve.isSuperExpression(); } if (mce.isImplicitThis() || isExplicitThisOrSuper) { if (mce.isImplicitThis()) { Expression ret = findStaticMethodImportFromModule(method, args); if (ret != null) { setSourcePosition(ret, mce); return ret; } if (method instanceof ConstantExpression && !inLeftExpression) { // could be a closure field String methodName = (String) ((ConstantExpression) method).getValue(); ret = findStaticFieldOrPropAccessorImportFromModule(methodName); if (ret != null) { ret = new MethodCallExpression(ret, "call", args); setSourcePosition(ret, mce); return ret; } } } else if (currentMethod != null && currentMethod.isStatic() && isExplicitSuper) { MethodCallExpression ret = new MethodCallExpression( new ClassExpression(currentClass.getSuperClass()), method, args); setSourcePosition(ret, mce); return ret; } if (method instanceof ConstantExpression) { ConstantExpression ce = (ConstantExpression) method; Object value = ce.getValue(); if (value instanceof String) { String methodName = (String) value; boolean lookForPossibleStaticMethod = !methodName.equals("call"); if (currentMethod != null && !currentMethod.isStatic()) { if (currentClass.hasPossibleMethod(methodName, args)) { lookForPossibleStaticMethod = false; } } if (!inClosure && (inSpecialConstructorCall || (lookForPossibleStaticMethod && currentClass.hasPossibleStaticMethod(methodName, args)))) { StaticMethodCallExpression smce = new StaticMethodCallExpression(currentClass, methodName, args); setSourcePosition(smce, mce); return smce; } } } } MethodCallExpression result = new MethodCallExpression(object, method, args); result.setSafe(mce.isSafe()); result.setImplicitThis(mce.isImplicitThis()); result.setSpreadSafe(mce.isSpreadSafe()); result.setMethodTarget(mce.getMethodTarget()); // GROOVY-6757 result.setGenericsTypes(mce.getGenericsTypes()); setSourcePosition(result, mce); return result; }
public void visitMethodCallExpression(MethodCallExpression call) { call.getObjectExpression().visit(this); call.getMethod().visit(this); call.getArguments().visit(this); }
Expression transformMethodCallExpression(final MethodCallExpression expr) { Expression objectExpression = expr.getObjectExpression(); if (expr.isSafe()) { MethodCallExpression notSafe = new MethodCallExpression(objectExpression, expr.getMethod(), expr.getArguments()); notSafe.copyNodeMetaData(expr); notSafe.setSpreadSafe(expr.isSpreadSafe()); notSafe.setSourcePosition(expr); notSafe.setMethodTarget(expr.getMethodTarget()); TernaryExpression texpr = new TernaryExpression( new BooleanExpression( new BinaryExpression( objectExpression, Token.newSymbol( "!=", objectExpression.getLineNumber(), objectExpression.getColumnNumber()), ConstantExpression.NULL)), notSafe, ConstantExpression.NULL); return staticCompilationTransformer.transform(texpr); } ClassNode type = staticCompilationTransformer .getTypeChooser() .resolveType(objectExpression, staticCompilationTransformer.getClassNode()); if (type != null && type.isArray()) { String method = expr.getMethodAsString(); ClassNode componentType = type.getComponentType(); if ("getAt".equals(method)) { Expression arguments = expr.getArguments(); if (arguments instanceof TupleExpression) { List<Expression> argList = ((TupleExpression) arguments).getExpressions(); if (argList.size() == 1) { Expression indexExpr = argList.get(0); ClassNode argType = staticCompilationTransformer .getTypeChooser() .resolveType(indexExpr, staticCompilationTransformer.getClassNode()); ClassNode indexType = ClassHelper.getWrapper(argType); if (componentType.isEnum() && ClassHelper.Number_TYPE == indexType) { // workaround for generated code in enums which use .next() returning a Number indexType = ClassHelper.Integer_TYPE; } if (argType != null && ClassHelper.Integer_TYPE == indexType) { BinaryExpression binaryExpression = new BinaryExpression( objectExpression, Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()), indexExpr); binaryExpression.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType); return staticCompilationTransformer.transform(binaryExpression); } } } } if ("putAt".equals(method)) { Expression arguments = expr.getArguments(); if (arguments instanceof TupleExpression) { List<Expression> argList = ((TupleExpression) arguments).getExpressions(); if (argList.size() == 2) { Expression indexExpr = argList.get(0); Expression objExpr = argList.get(1); ClassNode argType = staticCompilationTransformer .getTypeChooser() .resolveType(indexExpr, staticCompilationTransformer.getClassNode()); if (argType != null && ClassHelper.Integer_TYPE == ClassHelper.getWrapper(argType)) { BinaryExpression arrayGet = new BinaryExpression( objectExpression, Token.newSymbol("[", indexExpr.getLineNumber(), indexExpr.getColumnNumber()), indexExpr); arrayGet.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, componentType); BinaryExpression assignment = new BinaryExpression( arrayGet, Token.newSymbol("=", objExpr.getLineNumber(), objExpr.getColumnNumber()), objExpr); return staticCompilationTransformer.transform(assignment); } } } } } return staticCompilationTransformer.superTransform(expr); }
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); }