@Override
  public boolean handle(
      AnnotationValues<Synchronized> annotation, JCAnnotation ast, Node annotationNode) {
    Node methodNode = annotationNode.up();

    if (methodNode == null
        || methodNode.getKind() != Kind.METHOD
        || !(methodNode.get() instanceof JCMethodDecl)) {
      annotationNode.addError("@Synchronized is legal only on methods.");
      return true;
    }

    JCMethodDecl method = (JCMethodDecl) methodNode.get();

    if ((method.mods.flags & Flags.ABSTRACT) != 0) {
      annotationNode.addError("@Synchronized is legal only on concrete methods.");
      return true;
    }
    boolean isStatic = (method.mods.flags & Flags.STATIC) != 0;
    String lockName = annotation.getInstance().value();
    boolean autoMake = false;
    if (lockName.length() == 0) {
      autoMake = true;
      lockName = isStatic ? STATIC_LOCK_NAME : INSTANCE_LOCK_NAME;
    }

    TreeMaker maker = methodNode.getTreeMaker();

    if (fieldExists(lockName, methodNode) == MemberExistsResult.NOT_EXISTS) {
      if (!autoMake) {
        annotationNode.addError("The field " + new String(lockName) + " does not exist.");
        return true;
      }
      JCExpression objectType = chainDots(maker, methodNode, "java", "lang", "Object");
      // We use 'new Object[0];' because quite unlike 'new Object();', empty arrays *ARE*
      // serializable!
      JCNewArray newObjectArray =
          maker.NewArray(
              chainDots(maker, methodNode, "java", "lang", "Object"),
              List.<JCExpression>of(maker.Literal(TypeTags.INT, 0)),
              null);
      JCVariableDecl fieldDecl =
          maker.VarDef(
              maker.Modifiers(Flags.FINAL | (isStatic ? Flags.STATIC : 0)),
              methodNode.toName(lockName),
              objectType,
              newObjectArray);
      injectField(methodNode.up(), fieldDecl);
    }

    if (method.body == null) return false;

    JCExpression lockNode = maker.Ident(methodNode.toName(lockName));

    method.body = maker.Block(0, List.<JCStatement>of(maker.Synchronized(lockNode, method.body)));

    methodNode.rebuild();

    return true;
  }
 public void run() {
   Context ctx = new Context();
   Options options = Options.instance(ctx);
   outputKind.init(options);
   multiPolicy.init(options);
   xdiagsSource.init(options);
   xdiagsCompact.init(options);
   caretKind.init(options);
   sourceLineKind.init(options);
   String indentString = "";
   indentString = (summaryIndent == IndentKind.CUSTOM) ? "3" : "0";
   indentString += (detailsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
   indentString += (sourceIndent == IndentKind.CUSTOM) ? "|3" : "|0";
   indentString += (subdiagsIndent == IndentKind.CUSTOM) ? "|3" : "|0";
   options.put("diagsIndentation", indentString);
   MyLog log = new MyLog(ctx);
   JavacMessages messages = JavacMessages.instance(ctx);
   messages.add("tester");
   JCDiagnostic.Factory diags = JCDiagnostic.Factory.instance(ctx);
   log.useSource(new MyFileObject("This is a source line"));
   JCDiagnostic d =
       diags.error(null, log.currentSource(), posKind.pos(), errorKind.key(), "Hello!");
   if (multiKind != MultilineKind.NONE) {
     JCDiagnostic sub = diags.fragment(errorKind.key(), "Hello!");
     if (multiKind.isNested()) sub = new JCDiagnostic.MultilineDiagnostic(sub, List.of(sub));
     List<JCDiagnostic> subdiags = multiKind.isDouble() ? List.of(sub, sub) : List.of(sub);
     d = new JCDiagnostic.MultilineDiagnostic(d, subdiags);
   }
   String diag = log.getDiagnosticFormatter().format(d, messages.getCurrentLocale());
   checkOutput(diag);
 }
  private JCTree generateFieldInit() {
    long flags = (modifiers & Flags.STATIC);

    JCTree.JCExpression varInit = variableInit;
    if (toplevel) {
      varInit =
          owner
              .make()
              .NewArray(
                  attrTypeRaw,
                  List.<JCTree.JCExpression>nil(),
                  List.<JCTree.JCExpression>of(varInit));
    }
    JCTree.JCAssign init = owner.make().Assign(owner.makeUnquotedIdent(fieldName), varInit);
    return owner.make().Block(flags, List.<JCTree.JCStatement>of(owner.make().Exec(init)));
  }
Beispiel #4
0
  public JCMethodDecl generateBuilderMethod(
      String builderMethodName,
      String builderClassName,
      JavacNode type,
      List<JCTypeParameter> typeParams) {
    JavacTreeMaker maker = type.getTreeMaker();

    ListBuffer<JCExpression> typeArgs = new ListBuffer<JCExpression>();
    for (JCTypeParameter typeParam : typeParams) {
      typeArgs.append(maker.Ident(typeParam.name));
    }

    JCExpression call =
        maker.NewClass(
            null,
            List.<JCExpression>nil(),
            namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams),
            List.<JCExpression>nil(),
            null);
    JCStatement statement = maker.Return(call);

    JCBlock body = maker.Block(0, List.<JCStatement>of(statement));
    return maker.MethodDef(
        maker.Modifiers(Flags.STATIC | Flags.PUBLIC),
        type.toName(builderMethodName),
        namePlusTypeParamsToTypeReference(maker, type.toName(builderClassName), typeParams),
        copyTypeParams(maker, typeParams),
        List.<JCVariableDecl>nil(),
        List.<JCExpression>nil(),
        body,
        null);
  }
  private static void injectField(
      JavacNode typeNode, JCVariableDecl field, boolean addSuppressWarnings) {
    JCClassDecl type = (JCClassDecl) typeNode.get();

    if (addSuppressWarnings)
      addSuppressWarningsAll(field.mods, typeNode, field.pos, getGeneratedBy(field));

    List<JCTree> insertAfter = null;
    List<JCTree> insertBefore = type.defs;
    while (insertBefore.tail != null) {
      if (insertBefore.head instanceof JCVariableDecl) {
        JCVariableDecl f = (JCVariableDecl) insertBefore.head;
        if (isEnumConstant(f) || isGenerated(f)) {
          insertAfter = insertBefore;
          insertBefore = insertBefore.tail;
          continue;
        }
      }
      break;
    }
    List<JCTree> fieldEntry = List.<JCTree>of(field);
    fieldEntry.tail = insertBefore;
    if (insertAfter == null) {
      type.defs = fieldEntry;
    } else {
      insertAfter.tail = fieldEntry;
    }

    typeNode.add(field, Kind.FIELD);
  }
  private JCCompilationUnit ceylonParse(JavaFileObject filename, CharSequence readSource) {
    if (ceylonEnter.hasRun())
      throw new RuntimeException(
          "Trying to load new source file after CeylonEnter has been called: " + filename);
    try {
      String source = readSource.toString();
      ANTLRStringStream input = new ANTLRStringStream(source);
      CeylonLexer lexer = new CeylonLexer(input);

      CommonTokenStream tokens = new CommonTokenStream(lexer);

      CeylonParser parser = new CeylonParser(tokens);
      CompilationUnit cu = parser.compilationUnit();

      char[] chars = source.toCharArray();
      LineMap map = Position.makeLineMap(chars, chars.length, false);

      java.util.List<LexError> lexerErrors = lexer.getErrors();
      for (LexError le : lexerErrors) {
        printError(le, le.getMessage(), "ceylon.lexer", map);
      }

      java.util.List<ParseError> parserErrors = parser.getErrors();
      for (ParseError pe : parserErrors) {
        printError(pe, pe.getMessage(), "ceylon.parser", map);
      }

      if (lexer.getNumberOfSyntaxErrors() != 0) {
        log.error("ceylon.lexer.failed");
      } else if (parser.getNumberOfSyntaxErrors() != 0) {
        log.error("ceylon.parser.failed");
      } else {
        ModuleManager moduleManager = phasedUnits.getModuleManager();
        File sourceFile = new File(filename.toString());
        // FIXME: temporary solution
        VirtualFile file = vfs.getFromFile(sourceFile);
        VirtualFile srcDir = vfs.getFromFile(getSrcDir(sourceFile));
        // FIXME: this is bad in many ways
        String pkgName = getPackage(filename);
        // make a Package with no module yet, we will resolve them later
        com.redhat.ceylon.compiler.typechecker.model.Package p =
            modelLoader.findOrCreatePackage(null, pkgName == null ? "" : pkgName);
        PhasedUnit phasedUnit =
            new CeylonPhasedUnit(file, srcDir, cu, p, moduleManager, ceylonContext, filename, map);
        phasedUnits.addPhasedUnit(file, phasedUnit);
        gen.setMap(map);

        return gen.makeJCCompilationUnitPlaceholder(cu, filename, pkgName, phasedUnit);
      }
    } catch (Exception e) {
      log.error("ceylon", e.getMessage());
    }

    JCCompilationUnit result =
        make.TopLevel(List.<JCAnnotation>nil(), null, List.<JCTree>of(make.Erroneous()));
    result.sourcefile = filename;
    return result;
  }
  public JCNewClass build() {
    // Generate a subclass of Callable
    ListBuffer<JCTree> classBody = new ListBuffer<JCTree>();
    int numParams = paramLists.getParameters().size();
    int minimumParams = 0;
    for (Parameter p : paramLists.getParameters()) {
      if (p.isDefaulted() || p.isSequenced()) break;
      minimumParams++;
    }
    boolean isVariadic = minimumParams != numParams;
    if (parameterListTree != null) {
      // generate a method for each defaulted param
      for (Tree.Parameter p : parameterListTree.getParameters()) {
        if (p.getDefaultArgument() != null || p.getDeclarationModel().isSequenced()) {
          MethodDefinitionBuilder methodBuilder =
              gen.classGen().makeParamDefaultValueMethod(false, null, parameterListTree, p);
          classBody.append(methodBuilder.build());
        }
      }
    }

    // collect each parameter type from the callable type model rather than the declarations to get
    // them all bound
    java.util.List<ProducedType> parameterTypes = new ArrayList<ProducedType>(numParams);
    if (forwardCallTo != null) {
      for (int i = 0; i < numParams; i++)
        parameterTypes.add(gen.getParameterTypeOfCallable(typeModel, i));
    } else {
      // get them from our declaration
      for (Parameter p : paramLists.getParameters()) parameterTypes.add(p.getType());
    }

    // now generate a method for each supported minimum number of parameters below 4
    // which delegates to the $call$typed method if required
    for (int i = minimumParams, max = Math.min(numParams, 4); i < max; i++) {
      classBody.append(makeDefaultedCall(i, isVariadic, parameterTypes));
    }
    // generate the $call method for the max number of parameters,
    // which delegates to the $call$typed method if required
    classBody.append(makeDefaultedCall(numParams, isVariadic, parameterTypes));
    // generate the $call$typed method if required
    if (isVariadic && forwardCallTo == null)
      classBody.append(makeCallTypedMethod(body, parameterTypes));

    JCClassDecl classDef =
        gen.make().AnonymousClassDef(gen.make().Modifiers(0), classBody.toList());

    JCNewClass instance =
        gen.make()
            .NewClass(
                null,
                null,
                gen.makeJavaType(typeModel, JT_EXTENDS | JT_CLASS_NEW),
                List.<JCExpression>of(gen.make().Literal(typeModel.getProducedTypeName(true))),
                classDef);
    return instance;
  }
 /**
  * Constructs an {@code AbstractCallable} suitable for an anonymous function.
  *
  * @param parameterList2
  */
 public static CallableBuilder anonymous(
     CeylonTransformer gen,
     Tree.Expression expr,
     ParameterList parameterList,
     Tree.ParameterList parameterListTree,
     ProducedType callableTypeModel) {
   JCExpression transformedExpr = gen.expressionGen().transformExpression(expr);
   final List<JCStatement> stmts = List.<JCStatement>of(gen.make().Return(transformedExpr));
   return methodArgument(gen, callableTypeModel, parameterList, parameterListTree, stmts);
 }
 @Test
 public void inline() {
   ImportPolicy.bind(context, ImportPolicy.IMPORT_TOP_LEVEL);
   Symtab symtab = Symtab.instance(context);
   Type listType = symtab.listType;
   bind(
       new UTypeVar.Key("E"),
       TypeWithExpression.create(
           new ClassType(listType, List.<Type>of(symtab.stringType), listType.tsym)));
   assertInlines("List<String>", UTypeVarIdent.create("E"));
   assertEquals(ImmutableSet.of("java.util.List"), inliner.getImportsToAdd());
 }
Beispiel #10
0
 private static void addSuppressWarningsAll(
     JCModifiers mods, JavacNode node, int pos, JCTree source) {
   TreeMaker maker = node.getTreeMaker();
   JCExpression suppressWarningsType = chainDots(node, "java", "lang", "SuppressWarnings");
   JCLiteral allLiteral = maker.Literal("all");
   suppressWarningsType.pos = pos;
   allLiteral.pos = pos;
   JCAnnotation annotation =
       recursiveSetGeneratedBy(
           maker.Annotation(suppressWarningsType, List.<JCExpression>of(allLiteral)), source);
   annotation.pos = pos;
   mods.annotations = mods.annotations.append(annotation);
 }
Beispiel #11
0
  private JCStatement buildTryCatchBlock(
      JavacNode node, List<JCStatement> contents, String exception) {
    TreeMaker maker = node.getTreeMaker();

    JCBlock tryBlock = maker.Block(0, contents);

    JCExpression varType = chainDots(maker, node, exception.split("\\."));

    JCVariableDecl catchParam =
        maker.VarDef(maker.Modifiers(Flags.FINAL), node.toName("$ex"), varType, null);
    JCExpression lombokLombokSneakyThrowNameRef =
        chainDots(maker, node, "lombok", "Lombok", "sneakyThrow");
    JCBlock catchBody =
        maker.Block(
            0,
            List.<JCStatement>of(
                maker.Throw(
                    maker.Apply(
                        List.<JCExpression>nil(),
                        lombokLombokSneakyThrowNameRef,
                        List.<JCExpression>of(maker.Ident(node.toName("$ex")))))));

    return maker.Try(tryBlock, List.of(maker.Catch(catchParam, catchBody)), null);
  }
 private JCTree.JCBlock generateDefaultGetterBlock() {
   JCTree.JCExpression returnExpr = owner.makeUnquotedIdent(fieldName);
   if (toplevel) {
     returnExpr = owner.make().Indexed(returnExpr, owner.make().Literal(0));
   }
   JCTree.JCBlock block =
       owner.make().Block(0L, List.<JCTree.JCStatement>of(owner.make().Return(returnExpr)));
   if (toplevel) {
     JCTree.JCThrow throwStmt =
         owner
             .make()
             .Throw(
                 owner.makeNewClass(
                     owner.makeIdent(owner.syms().ceylonRecursiveInitializationExceptionType),
                     null));
     JCTree.JCBlock catchBlock = owner.make().Block(0, List.<JCTree.JCStatement>of(throwStmt));
     JCVariableDecl excepType =
         owner.makeVar("ex", owner.make().Type(owner.syms().nullPointerExceptionType), null);
     JCTree.JCCatch catcher = owner.make().Catch(excepType, catchBlock);
     JCTree.JCTry tryExpr = owner.make().Try(block, List.<JCTree.JCCatch>of(catcher), null);
     block = owner.make().Block(0L, List.<JCTree.JCStatement>of(tryExpr));
   }
   return block;
 }
  public JCMethodDecl createCanEqual(JavacNode typeNode, JCTree source) {
    /* public boolean canEqual(final java.lang.Object other) {
     *     return other instanceof Outer.Inner.MyType;
     * }
     */
    JavacTreeMaker maker = typeNode.getTreeMaker();

    JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.<JCAnnotation>nil());
    JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN);
    Name canEqualName = typeNode.toName("canEqual");
    JCExpression objectType = genJavaLangTypeRef(typeNode, "Object");
    Name otherName = typeNode.toName("other");
    long flags = JavacHandlerUtil.addFinalIfNeeded(Flags.PARAMETER, typeNode.getContext());
    List<JCVariableDecl> params =
        List.of(maker.VarDef(maker.Modifiers(flags), otherName, objectType, null));

    JCBlock body =
        maker.Block(
            0,
            List.<JCStatement>of(
                maker.Return(
                    maker.TypeTest(maker.Ident(otherName), createTypeReference(typeNode)))));

    return recursiveSetGeneratedBy(
        maker.MethodDef(
            mods,
            canEqualName,
            returnType,
            List.<JCTypeParameter>nil(),
            params,
            List.<JCExpression>nil(),
            body,
            null),
        source,
        typeNode.getContext());
  }
  private void initProcessorIterator(Context context, Iterable<? extends Processor> processors) {
    Log log = Log.instance(context);
    Iterator<? extends Processor> processorIterator;

    if (options.isSet(XPRINT)) {
      try {
        Processor processor = PrintingProcessor.class.newInstance();
        processorIterator = List.of(processor).iterator();
      } catch (Throwable t) {
        AssertionError assertError = new AssertionError("Problem instantiating PrintingProcessor.");
        assertError.initCause(t);
        throw assertError;
      }
    } else if (processors != null) {
      processorIterator = processors.iterator();
    } else {
      String processorNames = options.get(PROCESSOR);
      JavaFileManager fileManager = context.get(JavaFileManager.class);
      try {
        // If processorpath is not explicitly set, use the classpath.
        processorClassLoader =
            fileManager.hasLocation(ANNOTATION_PROCESSOR_PATH)
                ? fileManager.getClassLoader(ANNOTATION_PROCESSOR_PATH)
                : fileManager.getClassLoader(CLASS_PATH);

        /*
         * If the "-processor" option is used, search the appropriate
         * path for the named class.  Otherwise, use a service
         * provider mechanism to create the processor iterator.
         */
        if (processorNames != null) {
          processorIterator = new NameProcessIterator(processorNames, processorClassLoader, log);
        } else {
          processorIterator = new ServiceIterator(processorClassLoader, log);
        }
      } catch (SecurityException e) {
        /*
         * A security exception will occur if we can't create a classloader.
         * Ignore the exception if, with hindsight, we didn't need it anyway
         * (i.e. no processor was specified either explicitly, or implicitly,
         * in service configuration file.) Otherwise, we cannot continue.
         */
        processorIterator = handleServiceLoaderUnavailability("proc.cant.create.loader", e);
      }
    }
    discoveredProcs = new DiscoveredProcessors(processorIterator);
  }
Beispiel #15
0
  public JCMethodDecl generateBuildMethod(
      String name,
      Name staticName,
      JCExpression returnType,
      java.util.List<Name> fieldNames,
      JavacNode type,
      List<JCExpression> thrownExceptions) {
    JavacTreeMaker maker = type.getTreeMaker();

    JCExpression call;
    JCStatement statement;

    ListBuffer<JCExpression> args = new ListBuffer<JCExpression>();
    for (Name n : fieldNames) {
      args.append(maker.Ident(n));
    }

    if (staticName == null) {
      call = maker.NewClass(null, List.<JCExpression>nil(), returnType, args.toList(), null);
      statement = maker.Return(call);
    } else {
      ListBuffer<JCExpression> typeParams = new ListBuffer<JCExpression>();
      for (JCTypeParameter tp : ((JCClassDecl) type.get()).typarams) {
        typeParams.append(maker.Ident(tp.name));
      }

      JCExpression fn = maker.Select(maker.Ident(((JCClassDecl) type.up().get()).name), staticName);
      call = maker.Apply(typeParams.toList(), fn, args.toList());
      if (returnType instanceof JCPrimitiveTypeTree && CTC_VOID.equals(typeTag(returnType))) {
        statement = maker.Exec(call);
      } else {
        statement = maker.Return(call);
      }
    }

    JCBlock body = maker.Block(0, List.<JCStatement>of(statement));

    return maker.MethodDef(
        maker.Modifiers(Flags.PUBLIC),
        type.toName(name),
        returnType,
        List.<JCTypeParameter>nil(),
        List.<JCVariableDecl>nil(),
        thrownExceptions,
        body,
        null);
  }
 public JCStatement generateCompareFloatOrDouble(
     JCExpression thisDotField,
     JCExpression otherDotField,
     JavacTreeMaker maker,
     JavacNode node,
     boolean isDouble) {
   /* if (Float.compare(fieldName, other.fieldName) != 0) return false; */
   JCExpression clazz = genJavaLangTypeRef(node, isDouble ? "Double" : "Float");
   List<JCExpression> args = List.of(thisDotField, otherDotField);
   JCBinary compareCallEquals0 =
       maker.Binary(
           CTC_NOT_EQUAL,
           maker.Apply(
               List.<JCExpression>nil(), maker.Select(clazz, node.toName("compare")), args),
           maker.Literal(0));
   return maker.If(compareCallEquals0, returnBool(maker, false), null);
 }
 public JCTree.JCBlock generateDefaultSetterBlock() {
   JCTree.JCExpression fld;
   if (fieldName.equals(attrName)) {
     fld = owner.makeSelect("this", fieldName);
   } else {
     fld = owner.makeUnquotedIdent(fieldName);
   }
   if (toplevel) {
     fld = owner.make().Indexed(fld, owner.make().Literal(0));
   }
   return owner
       .make()
       .Block(
           0L,
           List.<JCTree.JCStatement>of(
               owner.make().Exec(owner.make().Assign(fld, owner.makeUnquotedIdent(attrName)))));
 }
Beispiel #18
0
 /**
  * Generates a new statement that checks if the given variable is null, and if so, throws a {@code
  * NullPointerException} with the variable name as message.
  */
 public static JCStatement generateNullCheck(TreeMaker treeMaker, JavacNode variable) {
   JCVariableDecl varDecl = (JCVariableDecl) variable.get();
   if (isPrimitive(varDecl.vartype)) return null;
   Name fieldName = varDecl.name;
   JCExpression npe = chainDots(variable, "java", "lang", "NullPointerException");
   JCTree exception =
       treeMaker.NewClass(
           null,
           List.<JCExpression>nil(),
           npe,
           List.<JCExpression>of(treeMaker.Literal(fieldName.toString())),
           null);
   JCStatement throwStatement = treeMaker.Throw(exception);
   return treeMaker.If(
       treeMaker.Binary(CTC_EQUAL, treeMaker.Ident(fieldName), treeMaker.Literal(CTC_BOT, null)),
       throwStatement,
       null);
 }
Beispiel #19
0
 public void visitAnnotation(JCAnnotation tree) {
   try {
     print("@");
     printExpr(tree.annotationType);
     if (tree.args.nonEmpty()) {
       print("(");
       if (tree.args.length() == 1 && tree.args.get(0) instanceof JCAssign) {
         JCExpression lhs = ((JCAssign) tree.args.get(0)).lhs;
         if (lhs instanceof JCIdent && ((JCIdent) lhs).name.toString().equals("value"))
           tree.args = List.of(((JCAssign) tree.args.get(0)).rhs);
       }
       printExprs(tree.args);
       print(")");
     }
   } catch (IOException e) {
     throw new UncheckedIOException(e);
   }
 }
Beispiel #20
0
  private boolean handleMethod(
      JavacNode annotation, JCMethodDecl method, Collection<String> exceptions) {
    JavacNode methodNode = annotation.up();

    if ((method.mods.flags & Flags.ABSTRACT) != 0) {
      annotation.addError("@SneakyThrows can only be used on concrete methods.");
      return true;
    }

    if (method.body == null) return false;

    List<JCStatement> contents = method.body.stats;

    for (String exception : exceptions) {
      contents = List.of(buildTryCatchBlock(methodNode, contents, exception));
    }

    method.body.stats = contents;
    methodNode.rebuild();

    return true;
  }
  public JCMethodDecl createEquals(
      JavacNode typeNode,
      List<JavacNode> fields,
      boolean callSuper,
      FieldAccess fieldAccess,
      boolean needsCanEqual,
      JCTree source) {
    JavacTreeMaker maker = typeNode.getTreeMaker();
    JCClassDecl type = (JCClassDecl) typeNode.get();

    Name oName = typeNode.toName("o");
    Name otherName = typeNode.toName("other");
    Name thisName = typeNode.toName("this");

    JCAnnotation overrideAnnotation =
        maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
    JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
    JCExpression objectType = genJavaLangTypeRef(typeNode, "Object");
    JCExpression returnType = maker.TypeIdent(CTC_BOOLEAN);

    long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());

    ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();
    final List<JCVariableDecl> params =
        List.of(
            maker.VarDef(maker.Modifiers(finalFlag | Flags.PARAMETER), oName, objectType, null));

    /* if (o == this) return true; */ {
      statements.append(
          maker.If(
              maker.Binary(CTC_EQUAL, maker.Ident(oName), maker.Ident(thisName)),
              returnBool(maker, true),
              null));
    }

    /* if (!(o instanceof Outer.Inner.MyType) return false; */ {
      JCUnary notInstanceOf =
          maker.Unary(CTC_NOT, maker.TypeTest(maker.Ident(oName), createTypeReference(typeNode)));
      statements.append(maker.If(notInstanceOf, returnBool(maker, false), null));
    }

    /* MyType<?> other = (MyType<?>) o; */ {
      if (!fields.isEmpty() || needsCanEqual) {
        final JCExpression selfType1, selfType2;
        ListBuffer<JCExpression> wildcards1 = new ListBuffer<JCExpression>();
        ListBuffer<JCExpression> wildcards2 = new ListBuffer<JCExpression>();
        for (int i = 0; i < type.typarams.length(); i++) {
          wildcards1.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
          wildcards2.append(maker.Wildcard(maker.TypeBoundKind(BoundKind.UNBOUND), null));
        }

        if (type.typarams.isEmpty()) {
          selfType1 = maker.Ident(type.name);
          selfType2 = maker.Ident(type.name);
        } else {
          selfType1 = maker.TypeApply(maker.Ident(type.name), wildcards1.toList());
          selfType2 = maker.TypeApply(maker.Ident(type.name), wildcards2.toList());
        }

        statements.append(
            maker.VarDef(
                maker.Modifiers(finalFlag),
                otherName,
                selfType1,
                maker.TypeCast(selfType2, maker.Ident(oName))));
      }
    }

    /* if (!other.canEqual((java.lang.Object) this)) return false; */ {
      if (needsCanEqual) {
        List<JCExpression> exprNil = List.nil();
        JCExpression thisRef = maker.Ident(thisName);
        JCExpression castThisRef = maker.TypeCast(genJavaLangTypeRef(typeNode, "Object"), thisRef);
        JCExpression equalityCheck =
            maker.Apply(
                exprNil,
                maker.Select(maker.Ident(otherName), typeNode.toName("canEqual")),
                List.of(castThisRef));
        statements.append(
            maker.If(maker.Unary(CTC_NOT, equalityCheck), returnBool(maker, false), null));
      }
    }

    /* if (!super.equals(o)) return false; */
    if (callSuper) {
      JCMethodInvocation callToSuper =
          maker.Apply(
              List.<JCExpression>nil(),
              maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("equals")),
              List.<JCExpression>of(maker.Ident(oName)));
      JCUnary superNotEqual = maker.Unary(CTC_NOT, callToSuper);
      statements.append(maker.If(superNotEqual, returnBool(maker, false), null));
    }

    Name thisDollar = typeNode.toName("this$");
    Name otherDollar = typeNode.toName("other$");
    for (JavacNode fieldNode : fields) {
      JCExpression fType = getFieldType(fieldNode, fieldAccess);
      JCExpression thisFieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
      JCExpression otherFieldAccessor =
          createFieldAccessor(maker, fieldNode, fieldAccess, maker.Ident(otherName));
      if (fType instanceof JCPrimitiveTypeTree) {
        switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
          case FLOAT:
            /* if (Float.compare(this.fieldName, other.fieldName) != 0) return false; */
            statements.append(
                generateCompareFloatOrDouble(
                    thisFieldAccessor, otherFieldAccessor, maker, typeNode, false));
            break;
          case DOUBLE:
            /* if (Double.compare(this.fieldName, other.fieldName) != 0) return false; */
            statements.append(
                generateCompareFloatOrDouble(
                    thisFieldAccessor, otherFieldAccessor, maker, typeNode, true));
            break;
          default:
            /* if (this.fieldName != other.fieldName) return false; */
            statements.append(
                maker.If(
                    maker.Binary(CTC_NOT_EQUAL, thisFieldAccessor, otherFieldAccessor),
                    returnBool(maker, false),
                    null));
            break;
        }
      } else if (fType instanceof JCArrayTypeTree) {
        /* if (!java.util.Arrays.deepEquals(this.fieldName, other.fieldName)) return false; //use equals for primitive arrays. */
        boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
        boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
        boolean useDeepEquals = multiDim || !primitiveArray;

        JCExpression eqMethod =
            chainDots(typeNode, "java", "util", "Arrays", useDeepEquals ? "deepEquals" : "equals");
        List<JCExpression> args = List.of(thisFieldAccessor, otherFieldAccessor);
        statements.append(
            maker.If(
                maker.Unary(CTC_NOT, maker.Apply(List.<JCExpression>nil(), eqMethod, args)),
                returnBool(maker, false),
                null));
      } else /* objects */ {
        /* final java.lang.Object this$fieldName = this.fieldName; */
        /* final java.lang.Object other$fieldName = other.fieldName; */
        /* if (this$fieldName == null ? other$fieldName != null : !this$fieldName.equals(other$fieldName)) return false;; */
        Name fieldName = ((JCVariableDecl) fieldNode.get()).name;
        Name thisDollarFieldName = thisDollar.append(fieldName);
        Name otherDollarFieldName = otherDollar.append(fieldName);

        statements.append(
            maker.VarDef(
                maker.Modifiers(finalFlag),
                thisDollarFieldName,
                genJavaLangTypeRef(typeNode, "Object"),
                thisFieldAccessor));
        statements.append(
            maker.VarDef(
                maker.Modifiers(finalFlag),
                otherDollarFieldName,
                genJavaLangTypeRef(typeNode, "Object"),
                otherFieldAccessor));

        JCExpression thisEqualsNull =
            maker.Binary(CTC_EQUAL, maker.Ident(thisDollarFieldName), maker.Literal(CTC_BOT, null));
        JCExpression otherNotEqualsNull =
            maker.Binary(
                CTC_NOT_EQUAL, maker.Ident(otherDollarFieldName), maker.Literal(CTC_BOT, null));
        JCExpression thisEqualsThat =
            maker.Apply(
                List.<JCExpression>nil(),
                maker.Select(maker.Ident(thisDollarFieldName), typeNode.toName("equals")),
                List.<JCExpression>of(maker.Ident(otherDollarFieldName)));
        JCExpression fieldsAreNotEqual =
            maker.Conditional(
                thisEqualsNull, otherNotEqualsNull, maker.Unary(CTC_NOT, thisEqualsThat));
        statements.append(maker.If(fieldsAreNotEqual, returnBool(maker, false), null));
      }
    }

    /* return true; */ {
      statements.append(returnBool(maker, true));
    }

    JCBlock body = maker.Block(0, statements.toList());
    return recursiveSetGeneratedBy(
        maker.MethodDef(
            mods,
            typeNode.toName("equals"),
            returnType,
            List.<JCTypeParameter>nil(),
            params,
            List.<JCExpression>nil(),
            body,
            null),
        source,
        typeNode.getContext());
  }
  public JCMethodDecl createHashCode(
      JavacNode typeNode,
      List<JavacNode> fields,
      boolean callSuper,
      FieldAccess fieldAccess,
      JCTree source) {
    JavacTreeMaker maker = typeNode.getTreeMaker();

    JCAnnotation overrideAnnotation =
        maker.Annotation(genJavaLangTypeRef(typeNode, "Override"), List.<JCExpression>nil());
    JCModifiers mods = maker.Modifiers(Flags.PUBLIC, List.of(overrideAnnotation));
    JCExpression returnType = maker.TypeIdent(CTC_INT);
    ListBuffer<JCStatement> statements = new ListBuffer<JCStatement>();

    Name primeName = typeNode.toName(PRIME_NAME);
    Name resultName = typeNode.toName(RESULT_NAME);
    long finalFlag = JavacHandlerUtil.addFinalIfNeeded(0L, typeNode.getContext());

    /* final int PRIME = X; */ {
      if (!fields.isEmpty() || callSuper) {
        statements.append(
            maker.VarDef(
                maker.Modifiers(finalFlag),
                primeName,
                maker.TypeIdent(CTC_INT),
                maker.Literal(HandlerUtil.primeForHashcode())));
      }
    }

    /* int result = 1; */ {
      statements.append(
          maker.VarDef(maker.Modifiers(0), resultName, maker.TypeIdent(CTC_INT), maker.Literal(1)));
    }

    if (callSuper) {
      JCMethodInvocation callToSuper =
          maker.Apply(
              List.<JCExpression>nil(),
              maker.Select(maker.Ident(typeNode.toName("super")), typeNode.toName("hashCode")),
              List.<JCExpression>nil());
      statements.append(createResultCalculation(typeNode, callToSuper));
    }

    Name dollar = typeNode.toName("$");
    for (JavacNode fieldNode : fields) {
      JCExpression fType = getFieldType(fieldNode, fieldAccess);
      JCExpression fieldAccessor = createFieldAccessor(maker, fieldNode, fieldAccess);
      if (fType instanceof JCPrimitiveTypeTree) {
        switch (((JCPrimitiveTypeTree) fType).getPrimitiveTypeKind()) {
          case BOOLEAN:
            /* this.fieldName ? X : Y */
            statements.append(
                createResultCalculation(
                    typeNode,
                    maker.Conditional(
                        fieldAccessor,
                        maker.Literal(HandlerUtil.primeForTrue()),
                        maker.Literal(HandlerUtil.primeForFalse()))));
            break;
          case LONG:
            {
              Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
              statements.append(
                  maker.VarDef(
                      maker.Modifiers(finalFlag),
                      dollarFieldName,
                      maker.TypeIdent(CTC_LONG),
                      fieldAccessor));
              statements.append(
                  createResultCalculation(
                      typeNode,
                      longToIntForHashCode(
                          maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
            }
            break;
          case FLOAT:
            /* Float.floatToIntBits(this.fieldName) */
            statements.append(
                createResultCalculation(
                    typeNode,
                    maker.Apply(
                        List.<JCExpression>nil(),
                        genJavaLangTypeRef(typeNode, "Float", "floatToIntBits"),
                        List.of(fieldAccessor))));
            break;
          case DOUBLE:
            {
              /* longToIntForHashCode(Double.doubleToLongBits(this.fieldName)) */
              Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
              JCExpression init =
                  maker.Apply(
                      List.<JCExpression>nil(),
                      genJavaLangTypeRef(typeNode, "Double", "doubleToLongBits"),
                      List.of(fieldAccessor));
              statements.append(
                  maker.VarDef(
                      maker.Modifiers(finalFlag),
                      dollarFieldName,
                      maker.TypeIdent(CTC_LONG),
                      init));
              statements.append(
                  createResultCalculation(
                      typeNode,
                      longToIntForHashCode(
                          maker, maker.Ident(dollarFieldName), maker.Ident(dollarFieldName))));
            }
            break;
          default:
          case BYTE:
          case SHORT:
          case INT:
          case CHAR:
            /* just the field */
            statements.append(createResultCalculation(typeNode, fieldAccessor));
            break;
        }
      } else if (fType instanceof JCArrayTypeTree) {
        /* java.util.Arrays.deepHashCode(this.fieldName) //use just hashCode() for primitive arrays. */
        boolean multiDim = ((JCArrayTypeTree) fType).elemtype instanceof JCArrayTypeTree;
        boolean primitiveArray = ((JCArrayTypeTree) fType).elemtype instanceof JCPrimitiveTypeTree;
        boolean useDeepHC = multiDim || !primitiveArray;

        JCExpression hcMethod =
            chainDots(typeNode, "java", "util", "Arrays", useDeepHC ? "deepHashCode" : "hashCode");
        statements.append(
            createResultCalculation(
                typeNode, maker.Apply(List.<JCExpression>nil(), hcMethod, List.of(fieldAccessor))));
      } else /* objects */ {
        /* final java.lang.Object $fieldName = this.fieldName; */
        /* $fieldName == null ? 0 : $fieldName.hashCode() */

        Name dollarFieldName = dollar.append(((JCVariableDecl) fieldNode.get()).name);
        statements.append(
            maker.VarDef(
                maker.Modifiers(finalFlag),
                dollarFieldName,
                genJavaLangTypeRef(typeNode, "Object"),
                fieldAccessor));

        JCExpression hcCall =
            maker.Apply(
                List.<JCExpression>nil(),
                maker.Select(maker.Ident(dollarFieldName), typeNode.toName("hashCode")),
                List.<JCExpression>nil());
        JCExpression thisEqualsNull =
            maker.Binary(CTC_EQUAL, maker.Ident(dollarFieldName), maker.Literal(CTC_BOT, null));
        statements.append(
            createResultCalculation(
                typeNode, maker.Conditional(thisEqualsNull, maker.Literal(0), hcCall)));
      }
    }

    /* return result; */ {
      statements.append(maker.Return(maker.Ident(resultName)));
    }

    JCBlock body = maker.Block(0, statements.toList());
    return recursiveSetGeneratedBy(
        maker.MethodDef(
            mods,
            typeNode.toName("hashCode"),
            returnType,
            List.<JCTypeParameter>nil(),
            List.<JCVariableDecl>nil(),
            List.<JCExpression>nil(),
            body,
            null),
        source,
        typeNode.getContext());
  }
Beispiel #23
0
/**
 * Container for all annotations (attributes in javac) on a Symbol.
 *
 * <p>This class is explicitly mutable. Its contents will change when attributes are annotated onto
 * the Symbol. However this class depends on the facts that List (in javac) is immutable.
 *
 * <p>An instance of this class can be in one of three states:
 *
 * <p>NOT_STARTED indicates that the Symbol this instance belongs to has not been annotated (yet).
 * Specifically if the declaration is not annotated this instance will never move past NOT_STARTED.
 * You can never go back to NOT_STARTED.
 *
 * <p>IN_PROGRESS annotations have been found on the declaration. Will be processed later. You can
 * reset to IN_PROGRESS. While IN_PROGRESS you can set the list of attributes (and this moves out of
 * the IN_PROGRESS state).
 *
 * <p>"unnamed" this Annotations contains some attributes, possibly the final set. While in this
 * state you can only prepend or append to the attributes not set it directly. You can also move
 * back to the IN_PROGRESS state using reset().
 *
 * <p><b>This is NOT part of any supported API. If you write code that depends on this, you do so at
 * your own risk. This code and its internal interfaces are subject to change or deletion without
 * notice.</b>
 */
public class Annotations {

  private static final List<Attribute.Compound> DECL_NOT_STARTED = List.of(null);
  private static final List<Attribute.Compound> DECL_IN_PROGRESS = List.of(null);

  /*
   * This field should never be null
   */
  private List<Attribute.Compound> attributes = DECL_NOT_STARTED;

  /*
   * This field should never be null
   */
  private List<Attribute.TypeCompound> type_attributes = List.<Attribute.TypeCompound>nil();

  /*
   * The Symbol this Annotations instance belongs to
   */
  private final Symbol sym;

  public Annotations(Symbol sym) {
    this.sym = sym;
  }

  public List<Attribute.Compound> getDeclarationAttributes() {
    return filterDeclSentinels(attributes);
  }

  public List<Attribute.TypeCompound> getTypeAttributes() {
    return type_attributes;
  }

  public void setDeclarationAttributes(List<Attribute.Compound> a) {
    Assert.check(pendingCompletion() || !isStarted());
    if (a == null) {
      throw new NullPointerException();
    }
    attributes = a;
  }

  public void setTypeAttributes(List<Attribute.TypeCompound> a) {
    if (a == null) {
      throw new NullPointerException();
    }
    type_attributes = a;
  }

  public void setAttributes(Annotations other) {
    if (other == null) {
      throw new NullPointerException();
    }
    setDeclarationAttributes(other.getDeclarationAttributes());
    setTypeAttributes(other.getTypeAttributes());
  }

  public void setDeclarationAttributesWithCompletion(
      final Annotate.AnnotateRepeatedContext<Attribute.Compound> ctx) {
    Assert.check(pendingCompletion() || (!isStarted() && sym.kind == PCK));
    this.setDeclarationAttributes(getAttributesForCompletion(ctx));
  }

  public void appendTypeAttributesWithCompletion(
      final Annotate.AnnotateRepeatedContext<Attribute.TypeCompound> ctx) {
    this.appendUniqueTypes(getAttributesForCompletion(ctx));
  }

  private <T extends Attribute.Compound> List<T> getAttributesForCompletion(
      final Annotate.AnnotateRepeatedContext<T> ctx) {

    Map<Symbol.TypeSymbol, ListBuffer<T>> annotated = ctx.annotated;
    boolean atLeastOneRepeated = false;
    List<T> buf = List.<T>nil();
    for (ListBuffer<T> lb : annotated.values()) {
      if (lb.size() == 1) {
        buf = buf.prepend(lb.first());
      } else { // repeated
        // This will break when other subtypes of Attributs.Compound
        // are introduced, because PlaceHolder is a subtype of TypeCompound.
        T res;
        @SuppressWarnings("unchecked")
        T ph = (T) new Placeholder<T>(ctx, lb.toList(), sym);
        res = ph;
        buf = buf.prepend(res);
        atLeastOneRepeated = true;
      }
    }

    if (atLeastOneRepeated) {
      // The Symbol s is now annotated with a combination of
      // finished non-repeating annotations and placeholders for
      // repeating annotations.
      //
      // We need to do this in two passes because when creating
      // a container for a repeating annotation we must
      // guarantee that the @Repeatable on the
      // contained annotation is fully annotated
      //
      // The way we force this order is to do all repeating
      // annotations in a pass after all non-repeating are
      // finished. This will work because @Repeatable
      // is non-repeating and therefore will be annotated in the
      // fist pass.

      // Queue a pass that will replace Attribute.Placeholders
      // with Attribute.Compound (made from synthesized containers).
      ctx.annotateRepeated(
          new Annotate.Annotator() {
            @Override
            public String toString() {
              return "repeated annotation pass of: " + sym + " in: " + sym.owner;
            }

            @Override
            public void enterAnnotation() {
              complete(ctx);
            }
          });
    }
    // Add non-repeating attributes
    return buf.reverse();
  }

  public Annotations reset() {
    attributes = DECL_IN_PROGRESS;
    return this;
  }

  public boolean isEmpty() {
    return !isStarted() || pendingCompletion() || attributes.isEmpty();
  }

  public boolean isTypesEmpty() {
    return type_attributes.isEmpty();
  }

  public boolean pendingCompletion() {
    return attributes == DECL_IN_PROGRESS;
  }

  public Annotations append(List<Attribute.Compound> l) {
    attributes = filterDeclSentinels(attributes);

    if (l.isEmpty()) {; // no-op
    } else if (attributes.isEmpty()) {
      attributes = l;
    } else {
      attributes = attributes.appendList(l);
    }
    return this;
  }

  public Annotations appendUniqueTypes(List<Attribute.TypeCompound> l) {
    if (l.isEmpty()) {; // no-op
    } else if (type_attributes.isEmpty()) {
      type_attributes = l;
    } else {
      // TODO: in case we expect a large number of annotations, this
      // might be inefficient.
      for (Attribute.TypeCompound tc : l) {
        if (!type_attributes.contains(tc)) type_attributes = type_attributes.append(tc);
      }
    }
    return this;
  }

  public Annotations prepend(List<Attribute.Compound> l) {
    attributes = filterDeclSentinels(attributes);

    if (l.isEmpty()) {; // no-op
    } else if (attributes.isEmpty()) {
      attributes = l;
    } else {
      attributes = attributes.prependList(l);
    }
    return this;
  }

  private List<Attribute.Compound> filterDeclSentinels(List<Attribute.Compound> a) {
    return (a == DECL_IN_PROGRESS || a == DECL_NOT_STARTED) ? List.<Attribute.Compound>nil() : a;
  }

  private boolean isStarted() {
    return attributes != DECL_NOT_STARTED;
  }

  private List<Attribute.Compound> getPlaceholders() {
    List<Attribute.Compound> res = List.<Attribute.Compound>nil();
    for (Attribute.Compound a : filterDeclSentinels(attributes)) {
      if (a instanceof Placeholder) {
        res = res.prepend(a);
      }
    }
    return res.reverse();
  }

  private List<Attribute.TypeCompound> getTypePlaceholders() {
    List<Attribute.TypeCompound> res = List.<Attribute.TypeCompound>nil();
    for (Attribute.TypeCompound a : type_attributes) {
      if (a instanceof Placeholder) {
        res = res.prepend(a);
      }
    }
    return res.reverse();
  }

  /*
   * Replace Placeholders for repeating annotations with their containers
   */
  private <T extends Attribute.Compound> void complete(Annotate.AnnotateRepeatedContext<T> ctx) {
    Log log = ctx.log;
    Env<AttrContext> env = ctx.env;
    JavaFileObject oldSource = log.useSource(env.toplevel.sourcefile);
    try {
      // TODO: can we reduce duplication in the following branches?
      if (ctx.isTypeCompound) {
        Assert.check(!isTypesEmpty());

        if (isTypesEmpty()) {
          return;
        }

        List<Attribute.TypeCompound> result = List.nil();
        for (Attribute.TypeCompound a : getTypeAttributes()) {
          if (a instanceof Placeholder) {
            @SuppressWarnings("unchecked")
            Placeholder<Attribute.TypeCompound> ph = (Placeholder<Attribute.TypeCompound>) a;
            Attribute.TypeCompound replacement = replaceOne(ph, ph.getRepeatedContext());

            if (null != replacement) {
              result = result.prepend(replacement);
            }
          } else {
            result = result.prepend(a);
          }
        }

        type_attributes = result.reverse();

        Assert.check(Annotations.this.getTypePlaceholders().isEmpty());
      } else {
        Assert.check(!pendingCompletion());

        if (isEmpty()) {
          return;
        }

        List<Attribute.Compound> result = List.nil();
        for (Attribute.Compound a : getDeclarationAttributes()) {
          if (a instanceof Placeholder) {
            @SuppressWarnings("unchecked")
            Attribute.Compound replacement = replaceOne((Placeholder<T>) a, ctx);

            if (null != replacement) {
              result = result.prepend(replacement);
            }
          } else {
            result = result.prepend(a);
          }
        }

        attributes = result.reverse();

        Assert.check(Annotations.this.getPlaceholders().isEmpty());
      }
    } finally {
      log.useSource(oldSource);
    }
  }

  private <T extends Attribute.Compound> T replaceOne(
      Placeholder<T> placeholder, Annotate.AnnotateRepeatedContext<T> ctx) {
    Log log = ctx.log;

    // Process repeated annotations
    T validRepeated = ctx.processRepeatedAnnotations(placeholder.getPlaceholderFor(), sym);

    if (validRepeated != null) {
      // Check that the container isn't manually
      // present along with repeated instances of
      // its contained annotation.
      ListBuffer<T> manualContainer = ctx.annotated.get(validRepeated.type.tsym);
      if (manualContainer != null) {
        log.error(
            ctx.pos.get(manualContainer.first()),
            "invalid.repeatable.annotation.repeated.and.container.present",
            manualContainer.first().type.tsym);
      }
    }

    // A null return will delete the Placeholder
    return validRepeated;
  }

  private static class Placeholder<T extends Attribute.Compound> extends Attribute.TypeCompound {

    private final Annotate.AnnotateRepeatedContext<T> ctx;
    private final List<T> placeholderFor;
    private final Symbol on;

    public Placeholder(Annotate.AnnotateRepeatedContext<T> ctx, List<T> placeholderFor, Symbol on) {
      super(
          on.type,
          List.<Pair<Symbol.MethodSymbol, Attribute>>nil(),
          ctx.isTypeCompound ? ((Attribute.TypeCompound) placeholderFor.head).position : null);
      this.ctx = ctx;
      this.placeholderFor = placeholderFor;
      this.on = on;
    }

    @Override
    public String toString() {
      return "<placeholder: " + placeholderFor + " on: " + on + ">";
    }

    public List<T> getPlaceholderFor() {
      return placeholderFor;
    }

    public Annotate.AnnotateRepeatedContext<T> getRepeatedContext() {
      return ctx;
    }
  }
}
  /**
   * Main method: compile a list of files, return all compiled classes
   *
   * @param filenames The names of all files to be compiled.
   */
  public List<ClassSymbol> compile(
      List<String> filenames,
      Map<String, String> origOptions,
      ClassLoader aptCL,
      AnnotationProcessorFactory providedFactory,
      java.util.Set<Class<? extends AnnotationProcessorFactory>> productiveFactories,
      java.util.Set<java.io.File> aggregateGenFiles)
      throws Throwable {
    // as a JavaCompiler can only be used once, throw an exception if
    // it has been used before.
    assert !hasBeenUsed : "attempt to reuse JavaCompiler";
    hasBeenUsed = true;

    this.aggregateGenFiles = aggregateGenFiles;

    long msec = System.currentTimeMillis();

    ListBuffer<ClassSymbol> classes = new ListBuffer<ClassSymbol>();
    try {
      JavacFileManager fm = (JavacFileManager) fileManager;
      // parse all files
      ListBuffer<JCCompilationUnit> trees = new ListBuffer<JCCompilationUnit>();
      for (List<String> l = filenames; l.nonEmpty(); l = l.tail) {
        if (classesAsDecls) {
          if (!l.head.endsWith(".java")) { // process as class file
            ClassSymbol cs = reader.enterClass(names.fromString(l.head));
            try {
              cs.complete();
            } catch (Symbol.CompletionFailure cf) {
              bark.aptError("CantFindClass", l);
              continue;
            }

            classes.append(cs); // add to list of classes
            continue;
          }
        }
        JavaFileObject fo = fm.getJavaFileObjectsFromStrings(List.of(l.head)).iterator().next();
        trees.append(parse(fo));
      }

      // enter symbols for all files
      List<JCCompilationUnit> roots = trees.toList();

      if (errorCount() == 0) {
        boolean prev = bark.setDiagnosticsIgnored(true);
        try {
          enter.main(roots);
        } finally {
          bark.setDiagnosticsIgnored(prev);
        }
      }

      if (errorCount() == 0) {
        apt.main(roots, classes, origOptions, aptCL, providedFactory, productiveFactories);
        genSourceFileNames.addAll(apt.getSourceFileNames());
        genClassFileNames.addAll(apt.getClassFileNames());
      }

    } catch (Abort ex) {
    }

    if (verbose) log.printVerbose("total", Long.toString(System.currentTimeMillis() - msec));

    chk.reportDeferredDiagnostics();

    printCount("error", errorCount());
    printCount("warn", warningCount());

    return classes.toList();
  }
 /** Adds a binary operator symbol. */
 final BinaryOperatorHelper addBinaryOperator(
     OperatorType arg1, OperatorType arg2, OperatorType res, int... opcode) {
   operatorSuppliers =
       operatorSuppliers.prepend(() -> makeOperator(name, List.of(arg1, arg2), res, opcode));
   return this;
 }
 Attribute enterAttributeValue(Type expected, JCExpression tree, Env<AttrContext> env) {
   // first, try completing the attribution value sym - if a completion
   // error is thrown, we should recover gracefully, and display an
   // ordinary resolution diagnostic.
   try {
     expected.tsym.complete();
   } catch (CompletionFailure e) {
     log.error(tree.pos(), "cant.resolve", Kinds.kindName(e.sym), e.sym);
     return new Attribute.Error(expected);
   }
   if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
     Type result = attr.attribExpr(tree, env, expected);
     if (result.isErroneous()) return new Attribute.Error(expected);
     if (result.constValue() == null) {
       log.error(tree.pos(), "attribute.value.must.be.constant");
       return new Attribute.Error(expected);
     }
     result = cfolder.coerce(result, expected);
     return new Attribute.Constant(expected, result.constValue());
   }
   if (expected.tsym == syms.classType.tsym) {
     Type result = attr.attribExpr(tree, env, expected);
     if (result.isErroneous()) return new Attribute.Error(expected);
     if (TreeInfo.name(tree) != names._class) {
       log.error(tree.pos(), "annotation.value.must.be.class.literal");
       return new Attribute.Error(expected);
     }
     return new Attribute.Class(types, (((JCFieldAccess) tree).selected).type);
   }
   if ((expected.tsym.flags() & Flags.ANNOTATION) != 0
       || types.isSameType(expected, syms.annotationType)) {
     if (tree.getTag() != JCTree.ANNOTATION) {
       log.error(tree.pos(), "annotation.value.must.be.annotation");
       expected = syms.errorType;
     }
     return enterAnnotation((JCAnnotation) tree, expected, env);
   }
   if (expected.tag == TypeTags.ARRAY) { // should really be isArray()
     if (tree.getTag() != JCTree.NEWARRAY) {
       tree = make.at(tree.pos).NewArray(null, List.<JCExpression>nil(), List.of(tree));
     }
     JCNewArray na = (JCNewArray) tree;
     if (na.elemtype != null) {
       log.error(na.elemtype.pos(), "new.not.allowed.in.annotation");
       return new Attribute.Error(expected);
     }
     ListBuffer<Attribute> buf = new ListBuffer<Attribute>();
     for (List<JCExpression> l = na.elems; l.nonEmpty(); l = l.tail) {
       buf.append(enterAttributeValue(types.elemtype(expected), l.head, env));
     }
     na.type = expected;
     return new Attribute.Array(expected, buf.toArray(new Attribute[buf.length()]));
   }
   if (expected.tag == TypeTags.CLASS && (expected.tsym.flags() & Flags.ENUM) != 0) {
     attr.attribExpr(tree, env, expected);
     Symbol sym = TreeInfo.symbol(tree);
     if (sym == null
         || TreeInfo.nonstaticSelect(tree)
         || sym.kind != Kinds.VAR
         || (sym.flags() & Flags.ENUM) == 0) {
       log.error(tree.pos(), "enum.annotation.must.be.enum.constant");
       return new Attribute.Error(expected);
     }
     VarSymbol enumerator = (VarSymbol) sym;
     return new Attribute.Enum(expected, enumerator);
   }
   if (!expected.isErroneous()) log.error(tree.pos(), "annotation.value.not.allowable.type");
   return new Attribute.Error(attr.attribExpr(tree, env, expected));
 }
  private JCCompilationUnit ceylonParse(JavaFileObject filename, CharSequence readSource) {
    if (ceylonEnter.hasRun())
      throw new RuntimeException(
          "Trying to load new source file after CeylonEnter has been called: " + filename);
    try {
      ModuleManager moduleManager = phasedUnits.getModuleManager();
      File sourceFile = new File(filename.getName());
      // FIXME: temporary solution
      VirtualFile file = vfs.getFromFile(sourceFile);
      VirtualFile srcDir = vfs.getFromFile(getSrcDir(sourceFile));

      String source = readSource.toString();
      char[] chars = source.toCharArray();
      LineMap map = Position.makeLineMap(chars, chars.length, false);

      PhasedUnit phasedUnit = null;

      PhasedUnit externalPhasedUnit = compilerDelegate.getExternalSourcePhasedUnit(srcDir, file);

      if (externalPhasedUnit != null) {
        phasedUnit = new CeylonPhasedUnit(externalPhasedUnit, filename, map);
        phasedUnits.addPhasedUnit(externalPhasedUnit.getUnitFile(), phasedUnit);
        gen.setMap(map);

        String pkgName = phasedUnit.getPackage().getQualifiedNameString();
        if ("".equals(pkgName)) {
          pkgName = null;
        }
        return gen.makeJCCompilationUnitPlaceholder(
            phasedUnit.getCompilationUnit(), filename, pkgName, phasedUnit);
      }
      if (phasedUnit == null) {
        ANTLRStringStream input = new ANTLRStringStream(source);
        CeylonLexer lexer = new CeylonLexer(input);

        CommonTokenStream tokens = new CommonTokenStream(lexer);

        CeylonParser parser = new CeylonParser(tokens);
        CompilationUnit cu = parser.compilationUnit();

        java.util.List<LexError> lexerErrors = lexer.getErrors();
        for (LexError le : lexerErrors) {
          printError(le, le.getMessage(), "ceylon.lexer", map);
        }

        java.util.List<ParseError> parserErrors = parser.getErrors();
        for (ParseError pe : parserErrors) {
          printError(pe, pe.getMessage(), "ceylon.parser", map);
        }

        if (lexerErrors.size() == 0 && parserErrors.size() == 0) {
          // FIXME: this is bad in many ways
          String pkgName = getPackage(filename);
          // make a Package with no module yet, we will resolve them later
          /*
           * Stef: see javadoc for findOrCreateModulelessPackage() for why this is here.
           */
          com.redhat.ceylon.compiler.typechecker.model.Package p =
              modelLoader.findOrCreateModulelessPackage(pkgName == null ? "" : pkgName);
          phasedUnit =
              new CeylonPhasedUnit(
                  file, srcDir, cu, p, moduleManager, ceylonContext, filename, map);
          phasedUnits.addPhasedUnit(file, phasedUnit);
          gen.setMap(map);

          return gen.makeJCCompilationUnitPlaceholder(cu, filename, pkgName, phasedUnit);
        }
      }
    } catch (RuntimeException e) {
      throw e;
    } catch (Exception e) {
      throw new RuntimeException(e);
    }

    JCCompilationUnit result =
        make.TopLevel(List.<JCAnnotation>nil(), null, List.<JCTree>of(make.Erroneous()));
    result.sourcefile = filename;
    return result;
  }
Beispiel #28
0
  void createSymbols() throws IOException {
    Set<String> legacy = getLegacyPackages();
    Set<String> legacyProprietary = getLegacyPackages();
    Set<String> documented = new HashSet<String>();
    Set<PackageSymbol> packages =
        ((JavacProcessingEnvironment) processingEnv).getSpecifiedPackages();
    String jarName = processingEnv.getOptions().get("com.sun.tools.javac.sym.Jar");
    if (jarName == null)
      throw new RuntimeException("Must use -Acom.sun.tools.javac.sym.Jar=LOCATION_OF_JAR");
    String destName = processingEnv.getOptions().get("com.sun.tools.javac.sym.Dest");
    if (destName == null)
      throw new RuntimeException("Must use -Acom.sun.tools.javac.sym.Dest=LOCATION_OF_JAR");

    for (PackageSymbol psym : packages) {
      String name = psym.getQualifiedName().toString();
      legacyProprietary.remove(name);
      documented.add(name);
    }

    JavaCompiler tool = ToolProvider.getSystemJavaCompiler();
    StandardJavaFileManager fm = tool.getStandardFileManager(null, null, null);
    Location jarLocation = StandardLocation.locationFor(jarName);
    File jarFile = new File(jarName);
    fm.setLocation(jarLocation, List.of(jarFile));
    fm.setLocation(StandardLocation.CLASS_PATH, List.<File>nil());
    fm.setLocation(StandardLocation.SOURCE_PATH, List.<File>nil());
    {
      ArrayList<File> bootClassPath = new ArrayList<File>();
      bootClassPath.add(jarFile);
      for (File path : fm.getLocation(StandardLocation.PLATFORM_CLASS_PATH)) {
        if (!new File(path.getName()).equals(new File("rt.jar"))) bootClassPath.add(path);
      }
      System.err.println("Using boot class path = " + bootClassPath);
      fm.setLocation(StandardLocation.PLATFORM_CLASS_PATH, bootClassPath);
    }
    // System.out.println(fm.getLocation(StandardLocation.PLATFORM_CLASS_PATH));
    File destDir = new File(destName);
    if (!destDir.exists())
      if (!destDir.mkdirs()) throw new RuntimeException("Could not create " + destDir);
    fm.setLocation(StandardLocation.CLASS_OUTPUT, List.of(destDir));
    Set<String> hiddenPackages = new HashSet<String>();
    Set<String> crisp = new HashSet<String>();
    List<String> options = List.of("-XDdev");
    // options = options.prepend("-doe");
    // options = options.prepend("-verbose");
    JavacTaskImpl task = (JavacTaskImpl) tool.getTask(null, fm, null, options, null, null);
    com.sun.tools.javac.main.JavaCompiler compiler =
        com.sun.tools.javac.main.JavaCompiler.instance(task.getContext());
    ClassReader reader = ClassReader.instance(task.getContext());
    ClassWriter writer = ClassWriter.instance(task.getContext());
    Symtab syms = Symtab.instance(task.getContext());
    Attribute.Compound proprietary =
        new Attribute.Compound(
            syms.proprietaryType, List.<Pair<Symbol.MethodSymbol, Attribute>>nil());

    Type.moreInfo = true;
    Pool pool = new Pool();
    for (JavaFileObject file : fm.list(jarLocation, "", EnumSet.of(CLASS), true)) {
      String className = fm.inferBinaryName(jarLocation, file);
      int index = className.lastIndexOf('.');
      String pckName = index == -1 ? "" : className.substring(0, index);
      boolean addLegacyAnnotation = false;
      if (documented.contains(pckName)) {
        if (!legacy.contains(pckName)) crisp.add(pckName);
        // System.out.println("Documented: " + className);
      } else if (legacyProprietary.contains(pckName)) {
        addLegacyAnnotation = true;
        // System.out.println("Legacy proprietary: " + className);
      } else {
        // System.out.println("Hidden " + className);
        hiddenPackages.add(pckName);
        continue;
      }
      TypeSymbol sym = (TypeSymbol) compiler.resolveIdent(className);
      if (sym.kind != Kinds.TYP) {
        if (className.indexOf('$') < 0) {
          System.err.println("Ignoring (other) " + className + " : " + sym);
          System.err.println("   " + sym.getClass().getSimpleName() + " " + sym.type);
        }
        continue;
      }
      sym.complete();
      if (sym.getEnclosingElement().getKind() != ElementKind.PACKAGE) {
        System.err.println("Ignoring (bad) " + sym.getQualifiedName());
        continue;
      }
      ClassSymbol cs = (ClassSymbol) sym;
      if (addLegacyAnnotation) {
        cs.attributes_field =
            (cs.attributes_field == null)
                ? List.of(proprietary)
                : cs.attributes_field.prepend(proprietary);
      }
      writeClass(pool, cs, writer);
    }

    if (false) {
      for (String pckName : crisp) System.out.println("Crisp: " + pckName);
      for (String pckName : hiddenPackages) System.out.println("Hidden: " + pckName);
      for (String pckName : legacyProprietary) System.out.println("Legacy proprietary: " + pckName);
      for (String pckName : documented) System.out.println("Documented: " + pckName);
    }
  }