duke@1: /*
ohair@798: * Copyright (c) 2003, 2010, Oracle and/or its affiliates. All rights reserved.
duke@1: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
duke@1: *
duke@1: * This code is free software; you can redistribute it and/or modify it
duke@1: * under the terms of the GNU General Public License version 2 only, as
ohair@554: * published by the Free Software Foundation. Oracle designates this
duke@1: * particular file as subject to the "Classpath" exception as provided
ohair@554: * by Oracle in the LICENSE file that accompanied this code.
duke@1: *
duke@1: * This code is distributed in the hope that it will be useful, but WITHOUT
duke@1: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
duke@1: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
duke@1: * version 2 for more details (a copy is included in the LICENSE file that
duke@1: * accompanied this code).
duke@1: *
duke@1: * You should have received a copy of the GNU General Public License version
duke@1: * 2 along with this work; if not, write to the Free Software Foundation,
duke@1: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
duke@1: *
ohair@554: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
ohair@554: * or visit www.oracle.com if you need additional information or have any
ohair@554: * questions.
duke@1: */
duke@1:
duke@1: package com.sun.tools.javac.comp;
duke@1:
duke@1: import com.sun.tools.javac.util.*;
duke@1: import com.sun.tools.javac.code.*;
duke@1: import com.sun.tools.javac.code.Symbol.*;
duke@1: import com.sun.tools.javac.tree.*;
duke@1: import com.sun.tools.javac.tree.JCTree.*;
duke@1:
duke@1: /** Enter annotations on symbols. Annotations accumulate in a queue,
duke@1: * which is processed at the top level of any set of recursive calls
duke@1: * requesting it be processed.
duke@1: *
jjg@581: *
This is NOT part of any supported API.
jjg@581: * If you write code that depends on this, you do so at your own risk.
duke@1: * This code and its internal interfaces are subject to change or
duke@1: * deletion without notice.
duke@1: */
duke@1: public class Annotate {
duke@1: protected static final Context.Key annotateKey =
duke@1: new Context.Key();
duke@1:
duke@1: public static Annotate instance(Context context) {
duke@1: Annotate instance = context.get(annotateKey);
duke@1: if (instance == null)
duke@1: instance = new Annotate(context);
duke@1: return instance;
duke@1: }
duke@1:
duke@1: final Attr attr;
duke@1: final TreeMaker make;
duke@1: final Log log;
duke@1: final Symtab syms;
jjg@113: final Names names;
duke@1: final Resolve rs;
duke@1: final Types types;
duke@1: final ConstFold cfolder;
duke@1: final Check chk;
duke@1:
duke@1: protected Annotate(Context context) {
duke@1: context.put(annotateKey, this);
duke@1: attr = Attr.instance(context);
duke@1: make = TreeMaker.instance(context);
duke@1: log = Log.instance(context);
duke@1: syms = Symtab.instance(context);
jjg@113: names = Names.instance(context);
duke@1: rs = Resolve.instance(context);
duke@1: types = Types.instance(context);
duke@1: cfolder = ConstFold.instance(context);
duke@1: chk = Check.instance(context);
duke@1: }
duke@1:
duke@1: /* ********************************************************************
duke@1: * Queue maintenance
duke@1: *********************************************************************/
duke@1:
duke@1: private int enterCount = 0;
duke@1:
duke@1: ListBuffer q = new ListBuffer();
duke@1:
duke@1: public void later(Annotator a) {
duke@1: q.append(a);
duke@1: }
duke@1:
duke@1: public void earlier(Annotator a) {
duke@1: q.prepend(a);
duke@1: }
duke@1:
duke@1: /** Called when the Enter phase starts. */
duke@1: public void enterStart() {
duke@1: enterCount++;
duke@1: }
duke@1:
duke@1: /** Called after the Enter phase completes. */
duke@1: public void enterDone() {
duke@1: enterCount--;
duke@1: flush();
duke@1: }
duke@1:
duke@1: public void flush() {
duke@1: if (enterCount != 0) return;
duke@1: enterCount++;
duke@1: try {
duke@1: while (q.nonEmpty())
duke@1: q.next().enterAnnotation();
duke@1: } finally {
duke@1: enterCount--;
duke@1: }
duke@1: }
duke@1:
duke@1: /** A client that has annotations to add registers an annotator,
duke@1: * the method it will use to add the annotation. There are no
duke@1: * parameters; any needed data should be captured by the
duke@1: * Annotator.
duke@1: */
duke@1: public interface Annotator {
duke@1: void enterAnnotation();
duke@1: String toString();
duke@1: }
duke@1:
duke@1:
duke@1: /* ********************************************************************
duke@1: * Compute an attribute from its annotation.
duke@1: *********************************************************************/
duke@1:
duke@1: /** Process a single compound annotation, returning its
duke@1: * Attribute. Used from MemberEnter for attaching the attributes
duke@1: * to the annotated symbol.
duke@1: */
duke@1: Attribute.Compound enterAnnotation(JCAnnotation a,
duke@1: Type expected,
duke@1: Env env) {
duke@1: // The annotation might have had its type attributed (but not checked)
duke@1: // by attr.attribAnnotationTypes during MemberEnter, in which case we do not
duke@1: // need to do it again.
duke@1: Type at = (a.annotationType.type != null ? a.annotationType.type
duke@1: : attr.attribType(a.annotationType, env));
duke@1: a.type = chk.checkType(a.annotationType.pos(), at, expected);
duke@1: if (a.type.isErroneous())
duke@1: return new Attribute.Compound(a.type, List.>nil());
duke@1: if ((a.type.tsym.flags() & Flags.ANNOTATION) == 0) {
duke@1: log.error(a.annotationType.pos(),
duke@1: "not.annotation.type", a.type.toString());
duke@1: return new Attribute.Compound(a.type, List.>nil());
duke@1: }
duke@1: List args = a.args;
duke@1: if (args.length() == 1 && args.head.getTag() != JCTree.ASSIGN) {
duke@1: // special case: elided "value=" assumed
duke@1: args.head = make.at(args.head.pos).
duke@1: Assign(make.Ident(names.value), args.head);
duke@1: }
duke@1: ListBuffer> buf =
duke@1: new ListBuffer>();
duke@1: for (List tl = args; tl.nonEmpty(); tl = tl.tail) {
duke@1: JCExpression t = tl.head;
duke@1: if (t.getTag() != JCTree.ASSIGN) {
duke@1: log.error(t.pos(), "annotation.value.must.be.name.value");
duke@1: continue;
duke@1: }
duke@1: JCAssign assign = (JCAssign)t;
duke@1: if (assign.lhs.getTag() != JCTree.IDENT) {
duke@1: log.error(t.pos(), "annotation.value.must.be.name.value");
duke@1: continue;
duke@1: }
duke@1: JCIdent left = (JCIdent)assign.lhs;
duke@1: Symbol method = rs.resolveQualifiedMethod(left.pos(),
duke@1: env,
duke@1: a.type,
duke@1: left.name,
duke@1: List.nil(),
duke@1: null);
duke@1: left.sym = method;
duke@1: left.type = method.type;
duke@1: if (method.owner != a.type.tsym)
duke@1: log.error(left.pos(), "no.annotation.member", left.name, a.type);
duke@1: Type result = method.type.getReturnType();
duke@1: Attribute value = enterAttributeValue(result, assign.rhs, env);
duke@1: if (!method.type.isErroneous())
duke@1: buf.append(new Pair
duke@1: ((MethodSymbol)method, value));
mcimadamore@676: t.type = result;
duke@1: }
duke@1: return new Attribute.Compound(a.type, buf.toList());
duke@1: }
duke@1:
duke@1: Attribute enterAttributeValue(Type expected,
duke@1: JCExpression tree,
duke@1: Env env) {
duke@1: if (expected.isPrimitive() || types.isSameType(expected, syms.stringType)) {
duke@1: Type result = attr.attribExpr(tree, env, expected);
duke@1: if (result.isErroneous())
duke@1: return new Attribute.Error(expected);
duke@1: if (result.constValue() == null) {
duke@1: log.error(tree.pos(), "attribute.value.must.be.constant");
duke@1: return new Attribute.Error(expected);
duke@1: }
duke@1: result = cfolder.coerce(result, expected);
duke@1: return new Attribute.Constant(expected, result.constValue());
duke@1: }
duke@1: if (expected.tsym == syms.classType.tsym) {
duke@1: Type result = attr.attribExpr(tree, env, expected);
duke@1: if (result.isErroneous())
duke@1: return new Attribute.Error(expected);
duke@1: if (TreeInfo.name(tree) != names._class) {
duke@1: log.error(tree.pos(), "annotation.value.must.be.class.literal");
duke@1: return new Attribute.Error(expected);
duke@1: }
duke@1: return new Attribute.Class(types,
duke@1: (((JCFieldAccess) tree).selected).type);
duke@1: }
duke@1: if ((expected.tsym.flags() & Flags.ANNOTATION) != 0) {
duke@1: if (tree.getTag() != JCTree.ANNOTATION) {
duke@1: log.error(tree.pos(), "annotation.value.must.be.annotation");
duke@1: expected = syms.errorType;
duke@1: }
duke@1: return enterAnnotation((JCAnnotation)tree, expected, env);
duke@1: }
duke@1: if (expected.tag == TypeTags.ARRAY) { // should really be isArray()
duke@1: if (tree.getTag() != JCTree.NEWARRAY) {
duke@1: tree = make.at(tree.pos).
duke@1: NewArray(null, List.nil(), List.of(tree));
duke@1: }
duke@1: JCNewArray na = (JCNewArray)tree;
duke@1: if (na.elemtype != null) {
duke@1: log.error(na.elemtype.pos(), "new.not.allowed.in.annotation");
duke@1: return new Attribute.Error(expected);
duke@1: }
duke@1: ListBuffer buf = new ListBuffer();
duke@1: for (List l = na.elems; l.nonEmpty(); l=l.tail) {
duke@1: buf.append(enterAttributeValue(types.elemtype(expected),
duke@1: l.head,
duke@1: env));
duke@1: }
mcimadamore@676: na.type = expected;
duke@1: return new Attribute.
duke@1: Array(expected, buf.toArray(new Attribute[buf.length()]));
duke@1: }
duke@1: if (expected.tag == TypeTags.CLASS &&
duke@1: (expected.tsym.flags() & Flags.ENUM) != 0) {
duke@1: attr.attribExpr(tree, env, expected);
duke@1: Symbol sym = TreeInfo.symbol(tree);
duke@1: if (sym == null ||
duke@1: TreeInfo.nonstaticSelect(tree) ||
duke@1: sym.kind != Kinds.VAR ||
duke@1: (sym.flags() & Flags.ENUM) == 0) {
duke@1: log.error(tree.pos(), "enum.annotation.must.be.enum.constant");
duke@1: return new Attribute.Error(expected);
duke@1: }
duke@1: VarSymbol enumerator = (VarSymbol) sym;
duke@1: return new Attribute.Enum(expected, enumerator);
duke@1: }
duke@1: if (!expected.isErroneous())
duke@1: log.error(tree.pos(), "annotation.value.not.allowable.type");
duke@1: return new Attribute.Error(attr.attribExpr(tree, env, expected));
duke@1: }
duke@1: }