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);
 }
 @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");
 }
  @Override
  protected boolean existsProperty(
      final PropertyExpression pexp,
      final boolean checkForReadOnly,
      final ClassCodeVisitorSupport visitor) {
    Expression objectExpression = pexp.getObjectExpression();
    ClassNode objectExpressionType = getType(objectExpression);
    final Reference<ClassNode> rType = new Reference<ClassNode>(objectExpressionType);
    ClassCodeVisitorSupport receiverMemoizer =
        new ClassCodeVisitorSupport() {
          @Override
          protected SourceUnit getSourceUnit() {
            return null;
          }

          public void visitField(final FieldNode node) {
            if (visitor != null) visitor.visitField(node);
            ClassNode declaringClass = node.getDeclaringClass();
            if (declaringClass != null) rType.set(declaringClass);
          }

          public void visitMethod(final MethodNode node) {
            if (visitor != null) visitor.visitMethod(node);
            ClassNode declaringClass = node.getDeclaringClass();
            if (declaringClass != null) rType.set(declaringClass);
          }

          @Override
          public void visitProperty(final PropertyNode node) {
            if (visitor != null) visitor.visitProperty(node);
            ClassNode declaringClass = node.getDeclaringClass();
            if (declaringClass != null) rType.set(declaringClass);
          }
        };
    boolean exists = super.existsProperty(pexp, checkForReadOnly, receiverMemoizer);
    if (exists) {
      if (objectExpression.getNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER) == null) {
        objectExpression.putNodeMetaData(StaticCompilationMetadataKeys.PROPERTY_OWNER, rType.get());
      }
      if (StaticTypeCheckingSupport.implementsInterfaceOrIsSubclassOf(
          objectExpressionType, ClassHelper.LIST_TYPE)) {
        objectExpression.putNodeMetaData(
            COMPONENT_TYPE, inferComponentType(objectExpressionType, ClassHelper.int_TYPE));
      }
    }
    return exists;
  }
  @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 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 MethodNode getTargetMethod(final Expression expression) {
   return (MethodNode) expression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
 }
 /**
  * Returns the original source expression (in case of constant inlining) or the input expression.
  *
  * @see org.codehaus.groovy.control.ResolveVisitor#transformInlineConstants
  * @see org.codehaus.groovy.control.ResolveVisitor#cloneConstantExpression
  */
 public static final Expression getNonInlinedExpression(Expression expression) {
   Expression original = expression.getNodeMetaData(ORIGINAL_EXPRESSION);
   return original != null ? original : expression;
 }
예제 #8
0
  Expression transformBinaryExpression(final BinaryExpression bin) {
    if (bin instanceof DeclarationExpression) {
      Expression optimized = transformDeclarationExpression(bin);
      if (optimized != null) {
        return optimized;
      }
    }
    Object[] list = bin.getNodeMetaData(BINARY_EXP_TARGET);
    Token operation = bin.getOperation();
    int operationType = operation.getType();
    Expression rightExpression = bin.getRightExpression();
    Expression leftExpression = bin.getLeftExpression();
    if (bin instanceof DeclarationExpression && leftExpression instanceof VariableExpression) {
      ClassNode declarationType = ((VariableExpression) leftExpression).getOriginType();
      if (rightExpression instanceof ConstantExpression) {
        ClassNode unwrapper = ClassHelper.getUnwrapper(declarationType);
        ClassNode wrapper = ClassHelper.getWrapper(declarationType);
        if (!rightExpression.getType().equals(declarationType)
            && wrapper.isDerivedFrom(ClassHelper.Number_TYPE)
            && WideningCategories.isDoubleCategory(unwrapper)) {
          ConstantExpression constant = (ConstantExpression) rightExpression;
          if (constant.getValue() != null) {
            return optimizeConstantInitialization(
                bin, operation, constant, leftExpression, declarationType);
          }
        }
      }
    }
    if (operationType == Types.EQUAL && leftExpression instanceof PropertyExpression) {
      MethodNode directMCT =
          leftExpression.getNodeMetaData(StaticTypesMarker.DIRECT_METHOD_CALL_TARGET);
      if (directMCT != null) {
        return transformPropertyAssignmentToSetterCall(
            (PropertyExpression) leftExpression, rightExpression, directMCT);
      }
    }
    if (operationType == Types.COMPARE_EQUAL || operationType == Types.COMPARE_NOT_EQUAL) {
      // let's check if one of the operands is the null constant
      CompareToNullExpression compareToNullExpression = null;
      if (isNullConstant(leftExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(rightExpression),
                operationType == Types.COMPARE_EQUAL);
      } else if (isNullConstant(rightExpression)) {
        compareToNullExpression =
            new CompareToNullExpression(
                staticCompilationTransformer.transform(leftExpression),
                operationType == Types.COMPARE_EQUAL);
      }
      if (compareToNullExpression != null) {
        compareToNullExpression.setSourcePosition(bin);
        return compareToNullExpression;
      }
    } else if (operationType == Types.KEYWORD_IN) {
      return convertInOperatorToTernary(bin, rightExpression, leftExpression);
    }
    if (list != null) {
      if (operationType == Types.COMPARE_TO) {
        StaticTypesTypeChooser typeChooser = staticCompilationTransformer.getTypeChooser();
        ClassNode classNode = staticCompilationTransformer.getClassNode();
        ClassNode leftType = typeChooser.resolveType(leftExpression, classNode);
        if (leftType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
          ClassNode rightType = typeChooser.resolveType(rightExpression, classNode);
          if (rightType.implementsInterface(ClassHelper.COMPARABLE_TYPE)) {
            Expression left = staticCompilationTransformer.transform(leftExpression);
            Expression right = staticCompilationTransformer.transform(rightExpression);
            MethodCallExpression call =
                new MethodCallExpression(left, "compareTo", new ArgumentListExpression(right));
            call.setImplicitThis(false);
            call.setMethodTarget(COMPARE_TO_METHOD);

            CompareIdentityExpression compareIdentity = new CompareIdentityExpression(left, right);
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.boolean_TYPE);
            TernaryExpression result =
                new TernaryExpression(
                    new BooleanExpression(compareIdentity), // a==b
                    CONSTANT_ZERO,
                    new TernaryExpression(
                        new BooleanExpression(new CompareToNullExpression(left, true)), // a==null
                        CONSTANT_MINUS_ONE,
                        new TernaryExpression(
                            new BooleanExpression(
                                new CompareToNullExpression(right, true)), // b==null
                            CONSTANT_ONE,
                            call)));
            compareIdentity.putNodeMetaData(
                StaticTypesMarker.INFERRED_RETURN_TYPE, ClassHelper.int_TYPE);
            result.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            TernaryExpression expr = (TernaryExpression) result.getFalseExpression();
            expr.putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            expr.getFalseExpression()
                .putNodeMetaData(StaticTypesMarker.INFERRED_TYPE, ClassHelper.int_TYPE);
            return result;
          }
        }
      }
      boolean isAssignment = StaticTypeCheckingSupport.isAssignment(operationType);
      MethodCallExpression call;
      MethodNode node = (MethodNode) list[0];
      String name = (String) list[1];
      Expression left = staticCompilationTransformer.transform(leftExpression);
      Expression right = staticCompilationTransformer.transform(rightExpression);
      BinaryExpression optimized = tryOptimizeCharComparison(left, right, bin);
      if (optimized != null) {
        optimized.removeNodeMetaData(BINARY_EXP_TARGET);
        return transformBinaryExpression(optimized);
      }
      call = new MethodCallExpression(left, name, new ArgumentListExpression(right));
      call.setImplicitThis(false);
      call.setMethodTarget(node);
      MethodNode adapter = StaticCompilationTransformer.BYTECODE_BINARY_ADAPTERS.get(operationType);
      if (adapter != null) {
        ClassExpression sba =
            new ClassExpression(StaticCompilationTransformer.BYTECODE_ADAPTER_CLASS);
        // replace with compareEquals
        call =
            new MethodCallExpression(sba, "compareEquals", new ArgumentListExpression(left, right));
        call.setMethodTarget(adapter);
        call.setImplicitThis(false);
      }
      if (!isAssignment) return call;
      // case of +=, -=, /=, ...
      // the method represents the operation type only, and we must add an assignment
      return new BinaryExpression(
          left, Token.newSymbol("=", operation.getStartLine(), operation.getStartColumn()), call);
    }
    if (bin.getOperation().getType() == Types.EQUAL
        && leftExpression instanceof TupleExpression
        && rightExpression instanceof ListExpression) {
      // multiple assignment
      ListOfExpressionsExpression cle = new ListOfExpressionsExpression();
      boolean isDeclaration = bin instanceof DeclarationExpression;
      List<Expression> leftExpressions = ((TupleExpression) leftExpression).getExpressions();
      List<Expression> rightExpressions = ((ListExpression) rightExpression).getExpressions();
      Iterator<Expression> leftIt = leftExpressions.iterator();
      Iterator<Expression> rightIt = rightExpressions.iterator();
      if (isDeclaration) {
        while (leftIt.hasNext()) {
          Expression left = leftIt.next();
          if (rightIt.hasNext()) {
            Expression right = rightIt.next();
            BinaryExpression bexp = new DeclarationExpression(left, bin.getOperation(), right);
            bexp.setSourcePosition(right);
            cle.addExpression(bexp);
          }
        }
      } else {
        // (next, result) = [ result, next+result ]
        // -->
        // def tmp1 = result
        // def tmp2 = next+result
        // next = tmp1
        // result = tmp2
        int size = rightExpressions.size();
        List<Expression> tmpAssignments = new ArrayList<Expression>(size);
        List<Expression> finalAssignments = new ArrayList<Expression>(size);
        for (int i = 0; i < Math.min(size, leftExpressions.size()); i++) {
          Expression left = leftIt.next();
          Expression right = rightIt.next();
          VariableExpression tmpVar = new VariableExpression("$tmpVar$" + tmpVarCounter++);
          BinaryExpression bexp = new DeclarationExpression(tmpVar, bin.getOperation(), right);
          bexp.setSourcePosition(right);
          tmpAssignments.add(bexp);
          bexp = new BinaryExpression(left, bin.getOperation(), new VariableExpression(tmpVar));
          bexp.setSourcePosition(left);
          finalAssignments.add(bexp);
        }
        for (Expression tmpAssignment : tmpAssignments) {
          cle.addExpression(tmpAssignment);
        }
        for (Expression finalAssignment : finalAssignments) {
          cle.addExpression(finalAssignment);
        }
      }
      return staticCompilationTransformer.transform(cle);
    }
    return staticCompilationTransformer.superTransform(bin);
  }