Пример #1
0
  private String genericsBounds(ClassNode theType, Set<String> visited) {
    String ret =
        theType.isArray() ? theType.getComponentType().getName() + "[]" : theType.getName();
    GenericsType[] genericsTypes = theType.getGenericsTypes();
    if (genericsTypes == null || genericsTypes.length == 0) return ret;
    // TODO instead of catching Object<T> here stop it from being placed into type in first place
    if (genericsTypes.length == 1
        && genericsTypes[0].isPlaceholder()
        && theType.getName().equals("java.lang.Object")) {
      return genericsTypes[0].getName();
    }
    ret += "<";
    for (int i = 0; i < genericsTypes.length; i++) {
      if (i != 0) ret += ", ";

      GenericsType type = genericsTypes[i];
      if (type.isPlaceholder() && visited.contains(type.getName())) {
        ret += type.getName();
      } else {
        ret += type.toString(visited);
      }
    }
    ret += ">";
    return ret;
  }
  @Override
  protected void assignToArray(
      Expression orig, Expression receiver, Expression index, Expression rhsValueLoader) {
    ClassNode current = getController().getClassNode();
    ClassNode arrayType = getController().getTypeChooser().resolveType(receiver, current);
    ClassNode arrayComponentType = arrayType.getComponentType();
    int operationType = getOperandType(arrayComponentType);
    BinaryExpressionWriter bew = binExpWriter[operationType];
    AsmClassGenerator acg = getController().getAcg();

    if (bew.arraySet(true) && arrayType.isArray()) {
      OperandStack operandStack = getController().getOperandStack();

      // load the array
      receiver.visit(acg);
      operandStack.doGroovyCast(arrayType);

      // load index
      index.visit(acg);
      operandStack.doGroovyCast(int_TYPE);

      // load rhs
      rhsValueLoader.visit(acg);
      operandStack.doGroovyCast(arrayComponentType);

      // store value in array
      bew.arraySet(false);

      // load return value && correct operand stack stack
      operandStack.remove(3);
      rhsValueLoader.visit(acg);
    } else {
      super.assignToArray(orig, receiver, index, rhsValueLoader);
    }
  }
Пример #3
0
 public static String getClassInternalName(ClassNode t) {
   if (t.isPrimaryClassNode()) {
     if (t.isArray()) return "[L" + getClassInternalName(t.getComponentType()) + ";";
     return getClassInternalName(t.getName());
   }
   return getClassInternalName(t.getTypeClass());
 }
 private Statement createConstructorStatement(
     ClassNode cNode,
     PropertyNode pNode,
     List<String> knownImmutableClasses,
     List<String> knownImmutables) {
   FieldNode fNode = pNode.getField();
   final ClassNode fieldType = fNode.getType();
   Statement statement = null;
   if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) {
     statement = createConstructorStatementArrayOrCloneable(fNode);
   } else if (isKnownImmutableClass(fieldType, knownImmutableClasses)
       || isKnownImmutable(pNode.getName(), knownImmutables)) {
     statement = createConstructorStatementDefault(fNode);
   } else if (fieldType.isDerivedFrom(DATE_TYPE)) {
     statement = createConstructorStatementDate(fNode);
   } else if (isOrImplements(fieldType, COLLECTION_TYPE)
       || fieldType.isDerivedFrom(COLLECTION_TYPE)
       || isOrImplements(fieldType, MAP_TYPE)
       || fieldType.isDerivedFrom(MAP_TYPE)) {
     statement = createConstructorStatementCollection(fNode);
   } else if (fieldType.isResolved()) {
     addError(
         createErrorMessage(cNode.getName(), fNode.getName(), fieldType.getName(), "compiling"),
         fNode);
     statement = EmptyStatement.INSTANCE;
   } else {
     statement = createConstructorStatementGuarded(cNode, fNode);
   }
   return statement;
 }
 private void printTypeWithoutBounds(PrintWriter out, ClassNode type) {
   if (type.isArray()) {
     printTypeWithoutBounds(out, type.getComponentType());
     out.print("[]");
   } else {
     printTypeName(out, type);
   }
 }
 private void printType(PrintWriter out, ClassNode type) {
   if (type.isArray()) {
     printType(out, type.getComponentType());
     out.print("[]");
   } else if (java5 && type.isGenericsPlaceHolder()) {
     out.print(type.getGenericsTypes()[0].getName());
   } else {
     printGenericsBounds(out, type, false);
   }
 }
Пример #7
0
 public static void doCast(MethodVisitor mv, ClassNode type) {
   if (type == ClassHelper.OBJECT_TYPE) return;
   if (ClassHelper.isPrimitiveType(type) && type != ClassHelper.VOID_TYPE) {
     unbox(mv, type);
   } else {
     mv.visitTypeInsn(
         CHECKCAST,
         type.isArray()
             ? BytecodeHelper.getTypeDescription(type)
             : BytecodeHelper.getClassInternalName(type.getName()));
   }
 }
 public static boolean implementsInterfaceOrIsSubclassOf(
     ClassNode type, ClassNode superOrInterface) {
   boolean result =
       type.equals(superOrInterface)
           || type.isDerivedFrom(superOrInterface)
           || type.implementsInterface(superOrInterface)
           || type == UNKNOWN_PARAMETER_TYPE;
   if (result) {
     return true;
   }
   if (superOrInterface instanceof WideningCategories.LowestUpperBoundClassNode) {
     WideningCategories.LowestUpperBoundClassNode cn =
         (WideningCategories.LowestUpperBoundClassNode) superOrInterface;
     result = implementsInterfaceOrIsSubclassOf(type, cn.getSuperClass());
     if (result) {
       for (ClassNode interfaceNode : cn.getInterfaces()) {
         result = type.implementsInterface(interfaceNode);
         if (!result) break;
       }
     }
     if (result) return true;
   } else if (superOrInterface instanceof UnionTypeClassNode) {
     UnionTypeClassNode union = (UnionTypeClassNode) superOrInterface;
     for (ClassNode delegate : union.getDelegates()) {
       if (implementsInterfaceOrIsSubclassOf(type, delegate)) return true;
     }
   }
   if (type.isArray() && superOrInterface.isArray()) {
     return implementsInterfaceOrIsSubclassOf(
         type.getComponentType(), superOrInterface.getComponentType());
   }
   if (GROOVY_OBJECT_TYPE.equals(superOrInterface)
       && !type.isInterface()
       && isBeingCompiled(type)) {
     return true;
   }
   return false;
 }
 private Statement createGetterBody(FieldNode fNode) {
   BlockStatement body = new BlockStatement();
   final ClassNode fieldType = fNode.getType();
   final Statement statement;
   if (fieldType.isArray() || isOrImplements(fieldType, CLONEABLE_TYPE)) {
     statement = createGetterBodyArrayOrCloneable(fNode);
   } else if (fieldType.isDerivedFrom(DATE_TYPE)) {
     statement = createGetterBodyDate(fNode);
   } else {
     statement = createGetterBodyDefault(fNode);
   }
   body.addStatement(statement);
   return body;
 }
Пример #10
0
  public static ClassNode nonGeneric(ClassNode type) {
    if (type.isUsingGenerics()) {
      final ClassNode nonGen = ClassHelper.makeWithoutCaching(type.getName());
      nonGen.setRedirect(type);
      nonGen.setGenericsTypes(null);
      nonGen.setUsingGenerics(false);
      return nonGen;
    }

    if (type.isArray()) {
      final ClassNode nonGen = ClassHelper.makeWithoutCaching(Object.class);
      nonGen.setUsingGenerics(false);
      return nonGen.makeArray();
    }

    return type.getPlainNodeReference();
  }
Пример #11
0
 /**
  * array types are special: eg.: String[]: classname: [Ljava/lang/String; int[]: [I
  *
  * @return the ASM type description
  */
 private static String getTypeDescription(ClassNode c, boolean end) {
   StringBuffer buf = new StringBuffer();
   ClassNode d = c;
   while (true) {
     if (ClassHelper.isPrimitiveType(d)) {
       char car;
       if (d == ClassHelper.int_TYPE) {
         car = 'I';
       } else if (d == ClassHelper.VOID_TYPE) {
         car = 'V';
       } else if (d == ClassHelper.boolean_TYPE) {
         car = 'Z';
       } else if (d == ClassHelper.byte_TYPE) {
         car = 'B';
       } else if (d == ClassHelper.char_TYPE) {
         car = 'C';
       } else if (d == ClassHelper.short_TYPE) {
         car = 'S';
       } else if (d == ClassHelper.double_TYPE) {
         car = 'D';
       } else if (d == ClassHelper.float_TYPE) {
         car = 'F';
       } else /* long */ {
         car = 'J';
       }
       buf.append(car);
       return buf.toString();
     } else if (d.isArray()) {
       buf.append('[');
       d = d.getComponentType();
     } else {
       buf.append('L');
       String name = d.getName();
       int len = name.length();
       for (int i = 0; i < len; ++i) {
         char car = name.charAt(i);
         buf.append(car == '.' ? '/' : car);
       }
       if (end) buf.append(';');
       return buf.toString();
     }
   }
 }
Пример #12
0
 /**
  * array types are special: eg.: String[]: classname: [Ljava.lang.String; Object: classname:
  * java.lang.Object int[] : classname: [I unlike getTypeDescription '.' is not replaced by '/'. it
  * seems that makes problems for the class loading if '.' is replaced by '/'
  *
  * @return the ASM type description for class loading
  */
 public static String getClassLoadingTypeDescription(ClassNode c) {
   StringBuffer buf = new StringBuffer();
   boolean array = false;
   while (true) {
     if (c.isArray()) {
       buf.append('[');
       c = c.getComponentType();
       array = true;
     } else {
       if (ClassHelper.isPrimitiveType(c)) {
         buf.append(getTypeDescription(c));
       } else {
         if (array) buf.append('L');
         buf.append(c.getName());
         if (array) buf.append(';');
       }
       return buf.toString();
     }
   }
 }
 private void checkGenericsUsage(ASTNode ref, ClassNode node) {
   if (node.isArray()) {
     checkGenericsUsage(ref, node.getComponentType());
   } else if (!node.isRedirectNode() && node.isUsingGenerics()) {
     addError(
         "A transform used a generics containing ClassNode "
             + node
             + " "
             + "for "
             + getRefDescriptor(ref)
             + "directly. You are not suppposed to do this. "
             + "Please create a new ClassNode refering to the old ClassNode "
             + "and use the new ClassNode instead of the old one. Otherwise "
             + "the compiler will create wrong descriptors and a potential "
             + "NullPointerException in TypeResolver in the OpenJDK. If this is "
             + "not your own doing, please report this bug to the writer of the "
             + "transform.",
         ref);
   }
 }
Пример #14
0
  /**
   * For a given classnode, fills in the supplied map with the parameterized types it defines.
   *
   * @param node
   * @param map
   */
  public static void extractPlaceholders(ClassNode node, Map<String, GenericsType> map) {
    if (node == null) return;

    if (node.isArray()) {
      extractPlaceholders(node.getComponentType(), map);
      return;
    }

    if (!node.isUsingGenerics() || !node.isRedirectNode()) return;
    GenericsType[] parameterized = node.getGenericsTypes();
    if (parameterized == null || parameterized.length == 0) return;
    GenericsType[] redirectGenericsTypes = node.redirect().getGenericsTypes();
    if (redirectGenericsTypes == null) redirectGenericsTypes = parameterized;
    for (int i = 0; i < redirectGenericsTypes.length; i++) {
      GenericsType redirectType = redirectGenericsTypes[i];
      if (redirectType.isPlaceholder()) {
        String name = redirectType.getName();
        if (!map.containsKey(name)) map.put(name, parameterized[i]);
      }
    }
  }
  @Override
  protected void evaluateBinaryExpression(final String message, BinaryExpression binExp) {
    int operation = removeAssignment(binExp.getOperation().getType());
    ClassNode current = getController().getClassNode();

    Expression leftExp = binExp.getLeftExpression();
    ClassNode leftTypeOrig = getController().getTypeChooser().resolveType(leftExp, current);
    ClassNode leftType = leftTypeOrig;
    Expression rightExp = binExp.getRightExpression();
    ClassNode rightType = getController().getTypeChooser().resolveType(rightExp, current);

    AsmClassGenerator acg = getController().getAcg();
    OperandStack os = getController().getOperandStack();

    if (operation == LEFT_SQUARE_BRACKET) {
      leftType = leftTypeOrig.getComponentType();
      int operationType = getOperandType(leftType);
      BinaryExpressionWriter bew = binExpWriter[operationType];
      if (leftTypeOrig.isArray() && isIntCastableType(rightExp) && bew.arrayGet(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(leftTypeOrig);
        rightExp.visit(acg);
        os.doGroovyCast(int_TYPE);
        bew.arrayGet(operation, false);
        os.replace(bew.getArrayGetResultType(), 2);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    } else if (operation == DIVIDE) {
      int operationType =
          getOperandType(getController().getTypeChooser().resolveType(binExp, current));
      BinaryExpressionWriter bew = binExpWriter[operationType];
      if (bew.writeDivision(true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getDevisionOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(bew.getDevisionOpResultType());
        bew.writeDivision(false);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    } else {
      int operationType = getOperandConversionType(leftType, rightType);
      BinaryExpressionWriter bew = binExpWriter[operationType];

      if (isShiftOperation(operation)
          && isIntCastableType(rightExp)
          && bew.write(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(int_TYPE);
        bew.write(operation, false);
      } else if (bew.write(operation, true)) {
        leftExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        rightExp.visit(acg);
        os.doGroovyCast(bew.getNormalOpResultType());
        bew.write(operation, false);
      } else {
        super.evaluateBinaryExpression(message, binExp);
      }
    }
  }
  public void evaluateEqual(BinaryExpression expression, boolean defineVariable) {
    AsmClassGenerator acg = controller.getAcg();
    CompileStack compileStack = controller.getCompileStack();
    OperandStack operandStack = controller.getOperandStack();
    Expression rightExpression = expression.getRightExpression();
    Expression leftExpression = expression.getLeftExpression();
    ClassNode lhsType =
        controller.getTypeChooser().resolveType(leftExpression, controller.getClassNode());

    if (defineVariable
        && rightExpression instanceof EmptyExpression
        && !(leftExpression instanceof TupleExpression)) {
      VariableExpression ve = (VariableExpression) leftExpression;
      BytecodeVariable var =
          compileStack.defineVariable(
              ve, controller.getTypeChooser().resolveType(ve, controller.getClassNode()), false);
      operandStack.loadOrStoreVariable(var, false);
      return;
    }

    // let's evaluate the RHS and store the result
    ClassNode rhsType;
    if (rightExpression instanceof ListExpression && lhsType.isArray()) {
      ListExpression list = (ListExpression) rightExpression;
      ArrayExpression array =
          new ArrayExpression(lhsType.getComponentType(), list.getExpressions());
      array.setSourcePosition(list);
      array.visit(acg);
    } else if (rightExpression instanceof EmptyExpression) {
      rhsType = leftExpression.getType();
      loadInitValue(rhsType);
    } else {
      rightExpression.visit(acg);
    }
    rhsType = operandStack.getTopOperand();

    boolean directAssignment = defineVariable && !(leftExpression instanceof TupleExpression);
    int rhsValueId;
    if (directAssignment) {
      VariableExpression var = (VariableExpression) leftExpression;
      if (var.isClosureSharedVariable() && ClassHelper.isPrimitiveType(rhsType)) {
        // GROOVY-5570: if a closure shared variable is a primitive type, it must be boxed
        rhsType = ClassHelper.getWrapper(rhsType);
        operandStack.box();
      }

      // ensure we try to unbox null to cause a runtime NPE in case we assign
      // null to a primitive typed variable, even if it is used only in boxed
      // form as it is closure shared
      if (var.isClosureSharedVariable()
          && ClassHelper.isPrimitiveType(var.getOriginType())
          && isNull(rightExpression)) {
        operandStack.doGroovyCast(var.getOriginType());
        // these two are never reached in bytecode and only there
        // to avoid verifyerrors and compiler infrastructure hazzle
        operandStack.box();
        operandStack.doGroovyCast(lhsType);
      }
      // normal type transformation
      if (!ClassHelper.isPrimitiveType(lhsType) && isNull(rightExpression)) {
        operandStack.replace(lhsType);
      } else {
        operandStack.doGroovyCast(lhsType);
      }
      rhsType = lhsType;
      rhsValueId = compileStack.defineVariable(var, lhsType, true).getIndex();
    } else {
      rhsValueId = compileStack.defineTemporaryVariable("$rhs", rhsType, true);
    }
    // TODO: if rhs is VariableSlotLoader already, then skip crating a new one
    BytecodeExpression rhsValueLoader = new VariableSlotLoader(rhsType, rhsValueId, operandStack);

    // assignment for subscript
    if (leftExpression instanceof BinaryExpression) {
      BinaryExpression leftBinExpr = (BinaryExpression) leftExpression;
      if (leftBinExpr.getOperation().getType() == Types.LEFT_SQUARE_BRACKET) {
        assignToArray(
            expression,
            leftBinExpr.getLeftExpression(),
            leftBinExpr.getRightExpression(),
            rhsValueLoader);
      }
      compileStack.removeVar(rhsValueId);
      return;
    }

    compileStack.pushLHS(true);

    // multiple declaration
    if (leftExpression instanceof TupleExpression) {
      TupleExpression tuple = (TupleExpression) leftExpression;
      int i = 0;
      for (Expression e : tuple.getExpressions()) {
        VariableExpression var = (VariableExpression) e;
        MethodCallExpression call =
            new MethodCallExpression(
                rhsValueLoader, "getAt", new ArgumentListExpression(new ConstantExpression(i)));
        call.visit(acg);
        i++;
        if (defineVariable) {
          operandStack.doGroovyCast(var);
          compileStack.defineVariable(var, true);
          operandStack.remove(1);
        } else {
          acg.visitVariableExpression(var);
        }
      }
    }
    // single declaration
    else if (defineVariable) {
      rhsValueLoader.visit(acg);
      operandStack.remove(1);
      compileStack.popLHS();
      return;
    }
    // normal assignment
    else {
      int mark = operandStack.getStackLength();
      // to leave a copy of the rightExpression value on the stack after the assignment.
      rhsValueLoader.visit(acg);
      TypeChooser typeChooser = controller.getTypeChooser();
      ClassNode targetType = typeChooser.resolveType(leftExpression, controller.getClassNode());
      operandStack.doGroovyCast(targetType);
      leftExpression.visit(acg);
      operandStack.remove(operandStack.getStackLength() - mark);
    }
    compileStack.popLHS();

    // return value of assignment
    rhsValueLoader.visit(acg);
    compileStack.removeVar(rhsValueId);
  }
  private boolean 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;
  }
 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);
 }
  @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);
  }
Пример #20
0
 private static boolean hasGenerics(ClassNode type) {
   return type.isArray() ? hasGenerics(type.getComponentType()) : type.getGenericsTypes() != null;
 }