@Override public void makeSingleArgumentCall( final Expression receiver, final String message, final Expression arguments) { TypeChooser typeChooser = controller.getTypeChooser(); ClassNode classNode = controller.getClassNode(); ClassNode rType = typeChooser.resolveType(receiver, classNode); ClassNode aType = typeChooser.resolveType(arguments, classNode); if (trySubscript(receiver, message, arguments, rType, aType)) { return; } // new try with flow type instead of declaration type rType = receiver.getNodeMetaData(StaticTypesMarker.INFERRED_TYPE); if (rType != null && trySubscript(receiver, message, arguments, rType, aType)) { return; } // todo: more cases throw new GroovyBugError( "At line " + receiver.getLineNumber() + " column " + receiver.getColumnNumber() + "\n" + "On receiver: " + receiver.getText() + " with message: " + message + " and arguments: " + arguments.getText() + "\n" + "This method should not have been called. Please try to create a simple example reproducing this error and file" + "a bug report at http://jira.codehaus.org/browse/GROOVY"); }
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 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); }
@Override protected void evaluateCompareExpression( final MethodCaller compareMethod, BinaryExpression binExp) { ClassNode current = getController().getClassNode(); TypeChooser typeChooser = getController().getTypeChooser(); Expression leftExp = binExp.getLeftExpression(); ClassNode leftType = typeChooser.resolveType(leftExp, current); Expression rightExp = binExp.getRightExpression(); ClassNode rightType = typeChooser.resolveType(rightExp, current); if (!doPrimitiveCompare(leftType, rightType, binExp)) { super.evaluateCompareExpression(compareMethod, binExp); } }
@Override public void visitConstructorCallExpression(final ConstructorCallExpression call) { super.visitConstructorCallExpression(call); MethodNode target = (MethodNode) call.getNodeMetaData(DIRECT_METHOD_CALL_TARGET); if (target == null && call.getLineNumber() > 0) { addError("Target constructor for constructor call expression hasn't been set", call); } else { if (target == null) { // try to find a target ArgumentListExpression argumentListExpression = InvocationWriter.makeArgumentList(call.getArguments()); List<Expression> expressions = argumentListExpression.getExpressions(); ClassNode[] args = new ClassNode[expressions.size()]; for (int i = 0; i < args.length; i++) { args[i] = typeChooser.resolveType(expressions.get(i), classNode); } MethodNode constructor = findMethodOrFail( call, call.isSuperCall() ? classNode.getSuperClass() : classNode, "<init>", args); call.putNodeMetaData(DIRECT_METHOD_CALL_TARGET, constructor); target = constructor; } } if (target != null) { memorizeInitialExpressions(target); } }
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); }
@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); }
@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; }
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); }