jjg@477: /* ohair@554: * Copyright (c) 2009, Oracle and/or its affiliates. All rights reserved. jjg@477: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. jjg@477: * jjg@477: * This code is free software; you can redistribute it and/or modify it jjg@477: * under the terms of the GNU General Public License version 2 only, as ohair@554: * published by the Free Software Foundation. Oracle designates this jjg@477: * particular file as subject to the "Classpath" exception as provided ohair@554: * by Oracle in the LICENSE file that accompanied this code. jjg@477: * jjg@477: * This code is distributed in the hope that it will be useful, but WITHOUT jjg@477: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or jjg@477: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License jjg@477: * version 2 for more details (a copy is included in the LICENSE file that jjg@477: * accompanied this code). jjg@477: * jjg@477: * You should have received a copy of the GNU General Public License version jjg@477: * 2 along with this work; if not, write to the Free Software Foundation, jjg@477: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. jjg@477: * 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. jjg@477: */ jjg@477: jjg@477: package com.sun.tools.javac.code; jjg@477: jjg@477: import javax.lang.model.element.ElementKind; jjg@477: jjg@477: import com.sun.tools.javac.code.Symbol.VarSymbol; jjg@477: import com.sun.tools.javac.tree.JCTree; jjg@477: import com.sun.tools.javac.tree.TreeInfo; jjg@477: import com.sun.tools.javac.tree.TreeScanner; jjg@477: import com.sun.tools.javac.tree.JCTree.*; jjg@477: import com.sun.tools.javac.util.Context; jjg@477: import com.sun.tools.javac.util.List; jjg@477: import com.sun.tools.javac.util.ListBuffer; jjg@477: jjg@477: /** jjg@477: * Contains operations specific to processing type annotations jjg@477: */ jjg@477: public class TypeAnnotations { jjg@477: private static final Context.Key key jjg@477: = new Context.Key(); jjg@477: jjg@477: public static TypeAnnotations instance(Context context) { jjg@477: TypeAnnotations instance = context.get(key); jjg@477: if (instance == null) jjg@477: instance = new TypeAnnotations(context); jjg@477: return instance; jjg@477: } jjg@477: jjg@477: protected TypeAnnotations(Context context) { jjg@477: context.put(key, this); jjg@477: } jjg@477: jjg@477: public void taFillAndLift(JCClassDecl tree, boolean visitBodies) { jjg@477: new TypeAnnotationPositions().scan(tree); jjg@477: new TypeAnnotationLift().scan(tree); jjg@477: } jjg@477: jjg@477: private static class TypeAnnotationPositions extends TreeScanner { jjg@477: jjg@477: private ListBuffer frames = ListBuffer.lb(); jjg@477: private void push(JCTree t) { frames = frames.prepend(t); } jjg@477: private JCTree pop() { return frames.next(); } jjg@477: private JCTree peek2() { return frames.toList().tail.head; } jjg@477: jjg@477: @Override jjg@477: public void scan(JCTree tree) { jjg@477: push(tree); jjg@477: super.scan(tree); jjg@477: pop(); jjg@477: } jjg@477: jjg@477: private boolean inClass = false; jjg@477: jjg@477: @Override jjg@477: public void visitClassDef(JCClassDecl tree) { jjg@477: if (!inClass) { jjg@477: // Do not recurse into nested and inner classes since jjg@477: // TransTypes.visitClassDef makes an invocation for each class jjg@477: // separately. jjg@477: inClass = true; jjg@477: try { jjg@477: super.visitClassDef(tree); jjg@477: } finally { jjg@477: inClass = false; jjg@477: } jjg@477: } jjg@477: } jjg@477: jjg@477: private TypeAnnotationPosition resolveFrame(JCTree tree, JCTree frame, jjg@477: List path, TypeAnnotationPosition p) { jjg@477: switch (frame.getKind()) { jjg@477: case TYPE_CAST: jjg@477: p.type = TargetType.TYPECAST; jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: jjg@477: case INSTANCE_OF: jjg@477: p.type = TargetType.INSTANCEOF; jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: jjg@477: case NEW_CLASS: jjg@477: p.type = TargetType.NEW; jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: jjg@477: case NEW_ARRAY: jjg@477: p.type = TargetType.NEW; jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: jjg@477: case CLASS: jjg@477: p.pos = frame.pos; jjg@477: if (((JCClassDecl)frame).extending == tree) { jjg@477: p.type = TargetType.CLASS_EXTENDS; jjg@477: p.type_index = -1; jjg@477: } else if (((JCClassDecl)frame).implementing.contains(tree)) { jjg@477: p.type = TargetType.CLASS_EXTENDS; jjg@477: p.type_index = ((JCClassDecl)frame).implementing.indexOf(tree); jjg@477: } else if (((JCClassDecl)frame).typarams.contains(tree)) { jjg@477: p.type = TargetType.CLASS_TYPE_PARAMETER; jjg@477: p.parameter_index = ((JCClassDecl)frame).typarams.indexOf(tree); jjg@477: } else jjg@477: throw new AssertionError(); jjg@477: return p; jjg@477: jjg@477: case METHOD: { jjg@477: JCMethodDecl frameMethod = (JCMethodDecl)frame; jjg@477: p.pos = frame.pos; jjg@477: if (frameMethod.receiverAnnotations.contains(tree)) jjg@477: p.type = TargetType.METHOD_RECEIVER; jjg@477: else if (frameMethod.thrown.contains(tree)) { jjg@477: p.type = TargetType.THROWS; jjg@477: p.type_index = frameMethod.thrown.indexOf(tree); jjg@477: } else if (((JCMethodDecl)frame).restype == tree) { jjg@477: p.type = TargetType.METHOD_RETURN_GENERIC_OR_ARRAY; jjg@477: } else if (frameMethod.typarams.contains(tree)) { jjg@477: p.type = TargetType.METHOD_TYPE_PARAMETER; jjg@477: p.parameter_index = frameMethod.typarams.indexOf(tree); jjg@477: } else jjg@477: throw new AssertionError(); jjg@477: return p; jjg@477: } jjg@477: case MEMBER_SELECT: { jjg@477: JCFieldAccess fieldFrame = (JCFieldAccess)frame; jjg@477: if ("class".contentEquals(fieldFrame.name)) { jjg@477: p.type = TargetType.CLASS_LITERAL; jjg@484: p.pos = TreeInfo.innermostType(fieldFrame.selected).pos; jjg@477: } else jjg@477: throw new AssertionError(); jjg@477: return p; jjg@477: } jjg@477: case PARAMETERIZED_TYPE: { jjg@477: TypeAnnotationPosition nextP; jjg@477: if (((JCTypeApply)frame).clazz == tree) jjg@477: nextP = p; // generic: RAW; noop jjg@477: else if (((JCTypeApply)frame).arguments.contains(tree)) jjg@477: p.location = p.location.prepend( jjg@477: ((JCTypeApply)frame).arguments.indexOf(tree)); jjg@477: else jjg@477: throw new AssertionError(); jjg@477: jjg@477: List newPath = path.tail; jjg@477: return resolveFrame(newPath.head, newPath.tail.head, newPath, p); jjg@477: } jjg@477: jjg@477: case ARRAY_TYPE: { jjg@477: p.location = p.location.prepend(0); jjg@477: List newPath = path.tail; jjg@477: return resolveFrame(newPath.head, newPath.tail.head, newPath, p); jjg@477: } jjg@477: jjg@477: case TYPE_PARAMETER: jjg@477: if (path.tail.tail.head.getTag() == JCTree.CLASSDEF) { jjg@477: JCClassDecl clazz = (JCClassDecl)path.tail.tail.head; jjg@477: p.type = TargetType.CLASS_TYPE_PARAMETER_BOUND; jjg@477: p.parameter_index = clazz.typarams.indexOf(path.tail.head); jjg@477: p.bound_index = ((JCTypeParameter)frame).bounds.indexOf(tree); jjg@477: } else if (path.tail.tail.head.getTag() == JCTree.METHODDEF) { jjg@477: JCMethodDecl method = (JCMethodDecl)path.tail.tail.head; jjg@477: p.type = TargetType.METHOD_TYPE_PARAMETER_BOUND; jjg@477: p.parameter_index = method.typarams.indexOf(path.tail.head); jjg@477: p.bound_index = ((JCTypeParameter)frame).bounds.indexOf(tree); jjg@477: } else jjg@477: throw new AssertionError(); jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: jjg@477: case VARIABLE: jjg@477: VarSymbol v = ((JCVariableDecl)frame).sym; jjg@477: p.pos = frame.pos; jjg@477: switch (v.getKind()) { jjg@477: case LOCAL_VARIABLE: jjg@477: p.type = TargetType.LOCAL_VARIABLE; break; jjg@477: case FIELD: jjg@477: p.type = TargetType.FIELD_GENERIC_OR_ARRAY; break; jjg@477: case PARAMETER: jjg@477: p.type = TargetType.METHOD_PARAMETER_GENERIC_OR_ARRAY; jjg@477: p.parameter_index = methodParamIndex(path, frame); jjg@477: break; jjg@477: default: throw new AssertionError(); jjg@477: } jjg@477: return p; jjg@477: jjg@477: case ANNOTATED_TYPE: { jjg@477: List newPath = path.tail; jjg@477: return resolveFrame(newPath.head, newPath.tail.head, jjg@477: newPath, p); jjg@477: } jjg@477: jjg@477: case METHOD_INVOCATION: { jjg@477: JCMethodInvocation invocation = (JCMethodInvocation)frame; jjg@477: if (!invocation.typeargs.contains(tree)) jjg@477: throw new AssertionError("{" + tree + "} is not an argument in the invocation: " + invocation); jjg@477: p.type = TargetType.METHOD_TYPE_ARGUMENT; jjg@477: p.pos = invocation.pos; jjg@477: p.type_index = invocation.typeargs.indexOf(tree); jjg@477: return p; jjg@477: } jjg@477: jjg@477: case EXTENDS_WILDCARD: jjg@477: case SUPER_WILDCARD: { jjg@477: p.type = TargetType.WILDCARD_BOUND; jjg@477: List newPath = path.tail; jjg@477: jjg@477: TypeAnnotationPosition wildcard = jjg@477: resolveFrame(newPath.head, newPath.tail.head, newPath, jjg@477: new TypeAnnotationPosition()); jjg@477: if (!wildcard.location.isEmpty()) jjg@477: wildcard.type = wildcard.type.getGenericComplement(); jjg@477: p.wildcard_position = wildcard; jjg@477: p.pos = frame.pos; jjg@477: return p; jjg@477: } jjg@477: } jjg@477: return p; jjg@477: } jjg@477: jjg@477: private void setTypeAnnotationPos(List annotations, TypeAnnotationPosition position) { jjg@477: for (JCTypeAnnotation anno : annotations) { jjg@477: anno.annotation_position = position; jjg@477: anno.attribute_field.position = position; jjg@477: } jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitNewArray(JCNewArray tree) { jjg@477: findPosition(tree, tree, tree.annotations); jjg@477: int dimAnnosCount = tree.dimAnnotations.size(); jjg@477: jjg@477: // handle annotations associated with dimentions jjg@477: for (int i = 0; i < dimAnnosCount; ++i) { jjg@477: TypeAnnotationPosition p = new TypeAnnotationPosition(); jjg@477: p.type = TargetType.NEW_GENERIC_OR_ARRAY; jjg@477: p.pos = tree.pos; jjg@477: p.location = p.location.append(i); jjg@477: setTypeAnnotationPos(tree.dimAnnotations.get(i), p); jjg@477: } jjg@477: jjg@477: // handle "free" annotations jjg@477: int i = dimAnnosCount == 0 ? 0 : dimAnnosCount - 1; jjg@477: JCExpression elemType = tree.elemtype; jjg@477: while (elemType != null) { jjg@477: if (elemType.getTag() == JCTree.ANNOTATED_TYPE) { jjg@477: JCAnnotatedType at = (JCAnnotatedType)elemType; jjg@477: TypeAnnotationPosition p = new TypeAnnotationPosition(); jjg@477: p.type = TargetType.NEW_GENERIC_OR_ARRAY; jjg@477: p.pos = tree.pos; jjg@477: p.location = p.location.append(i); jjg@477: setTypeAnnotationPos(at.annotations, p); jjg@477: elemType = at.underlyingType; jjg@477: } else if (elemType.getTag() == JCTree.TYPEARRAY) { jjg@477: ++i; jjg@477: elemType = ((JCArrayTypeTree)elemType).elemtype; jjg@477: } else jjg@477: break; jjg@477: } jjg@477: jjg@477: // find annotations locations of initializer elements jjg@477: scan(tree.elems); jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitAnnotatedType(JCAnnotatedType tree) { jjg@477: findPosition(tree, peek2(), tree.annotations); jjg@477: super.visitAnnotatedType(tree); jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitMethodDef(JCMethodDecl tree) { jjg@477: TypeAnnotationPosition p = new TypeAnnotationPosition(); jjg@477: p.type = TargetType.METHOD_RECEIVER; jjg@477: setTypeAnnotationPos(tree.receiverAnnotations, p); jjg@477: super.visitMethodDef(tree); jjg@477: } jjg@477: @Override jjg@477: public void visitTypeParameter(JCTypeParameter tree) { jjg@477: findPosition(tree, peek2(), tree.annotations); jjg@477: super.visitTypeParameter(tree); jjg@477: } jjg@477: jjg@477: void findPosition(JCTree tree, JCTree frame, List annotations) { jjg@477: if (!annotations.isEmpty()) { jjg@477: TypeAnnotationPosition p = jjg@477: resolveFrame(tree, frame, frames.toList(), jjg@477: new TypeAnnotationPosition()); jjg@477: if (!p.location.isEmpty()) jjg@477: p.type = p.type.getGenericComplement(); jjg@477: setTypeAnnotationPos(annotations, p); jjg@477: } jjg@477: } jjg@477: jjg@477: private int methodParamIndex(List path, JCTree param) { jjg@477: List curr = path; jjg@477: if (curr.head != param) jjg@477: curr = path.tail; jjg@477: JCMethodDecl method = (JCMethodDecl)curr.tail.head; jjg@477: return method.params.indexOf(param); jjg@477: } jjg@477: } jjg@477: jjg@477: private static class TypeAnnotationLift extends TreeScanner { jjg@477: List recordedTypeAnnotations = List.nil(); jjg@477: jjg@477: boolean isInner = false; jjg@477: @Override jjg@477: public void visitClassDef(JCClassDecl tree) { jjg@477: if (isInner) { jjg@477: // tree is an inner class tree. stop now. jjg@477: // TransTypes.visitClassDef makes an invocation for each class jjg@477: // separately. jjg@477: return; jjg@477: } jjg@477: isInner = true; jjg@477: List prevTAs = recordedTypeAnnotations; jjg@477: recordedTypeAnnotations = List.nil(); jjg@477: try { jjg@477: super.visitClassDef(tree); jjg@477: } finally { jjg@477: tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations); jjg@477: recordedTypeAnnotations = prevTAs; jjg@477: } jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitMethodDef(JCMethodDecl tree) { jjg@477: List prevTAs = recordedTypeAnnotations; jjg@477: recordedTypeAnnotations = List.nil(); jjg@477: try { jjg@477: super.visitMethodDef(tree); jjg@477: } finally { jjg@477: tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations); jjg@477: recordedTypeAnnotations = prevTAs; jjg@477: } jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitVarDef(JCVariableDecl tree) { jjg@477: List prevTAs = recordedTypeAnnotations; jjg@477: recordedTypeAnnotations = List.nil(); jjg@477: ElementKind kind = tree.sym.getKind(); jjg@477: if (kind == ElementKind.LOCAL_VARIABLE && tree.mods.annotations.nonEmpty()) { jjg@477: // need to lift the annotations jjg@477: TypeAnnotationPosition position = new TypeAnnotationPosition(); jjg@477: position.pos = tree.pos; jjg@477: position.type = TargetType.LOCAL_VARIABLE; jjg@477: for (Attribute.Compound attribute : tree.sym.attributes_field) { jjg@477: Attribute.TypeCompound tc = jjg@477: new Attribute.TypeCompound(attribute.type, attribute.values, position); jjg@477: recordedTypeAnnotations = recordedTypeAnnotations.append(tc); jjg@477: } jjg@477: } jjg@477: try { jjg@477: super.visitVarDef(tree); jjg@477: } finally { jjg@477: if (kind.isField() || kind == ElementKind.LOCAL_VARIABLE) jjg@477: tree.sym.typeAnnotations = tree.sym.typeAnnotations.appendList(recordedTypeAnnotations); jjg@477: recordedTypeAnnotations = kind.isField() ? prevTAs : prevTAs.appendList(recordedTypeAnnotations); jjg@477: } jjg@477: } jjg@477: jjg@477: @Override jjg@477: public void visitApply(JCMethodInvocation tree) { jjg@477: scan(tree.meth); jjg@477: scan(tree.typeargs); jjg@477: scan(tree.args); jjg@477: } jjg@477: jjg@477: public void visitAnnotation(JCAnnotation tree) { jjg@477: if (tree instanceof JCTypeAnnotation) jjg@477: recordedTypeAnnotations = recordedTypeAnnotations.append(((JCTypeAnnotation)tree).attribute_field); jjg@477: super.visitAnnotation(tree); jjg@477: } jjg@477: } jjg@477: jjg@477: }