| /******************************************************************************* |
| * Copyright (c) 2013 GK Software AG. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * Stephan Herrmann - initial API and implementation |
| *******************************************************************************/ |
| package org.aspectj.org.eclipse.jdt.internal.compiler.classfmt; |
| |
| import org.aspectj.org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; |
| import org.aspectj.org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; |
| |
| /** |
| * A TypeAnnotationWalker is initialized with all type annotations found at a given element. |
| * It can be used to walk into the types at the given element and finally answer the |
| * actual annotations at any node of the walk. |
| * |
| * The walker is implemented as immutable objects. During the walk either new instances |
| * are created, or the current instance is shared if no difference is encountered. |
| */ |
| public class TypeAnnotationWalker { |
| |
| public static final IBinaryAnnotation[] NO_ANNOTATIONS = new IBinaryAnnotation[0]; |
| |
| /** |
| * A no-effect annotation walker, all walking methods are implemented as identity-functions. |
| * At the end of any walk an empty array of annotations is returned. |
| */ |
| public static final TypeAnnotationWalker EMPTY_ANNOTATION_WALKER = new TypeAnnotationWalker(new IBinaryTypeAnnotation[0], 0L) { |
| public TypeAnnotationWalker toField() { return this; } |
| public TypeAnnotationWalker toTarget(int targetType) { return this; } |
| public TypeAnnotationWalker toThrows(int rank) { return this; } |
| public TypeAnnotationWalker toTypeArgument(int rank) { return this; } |
| public TypeAnnotationWalker toMethodParameter(short index) { return this; } |
| public TypeAnnotationWalker toSupertype(short index) { return this; } |
| public TypeAnnotationWalker toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) { return this; } |
| public TypeAnnotationWalker toTypeBound(short boundIndex) { return this; } |
| public TypeAnnotationWalker toTypeParameter(boolean isClassTypeParameter, int rank) { return this; } |
| public TypeAnnotationWalker toNextDetail(int detailKind) { return this; } |
| public IBinaryAnnotation[] getAnnotationsAtCursor() { return NO_ANNOTATIONS; } |
| }; |
| |
| final private IBinaryTypeAnnotation[] typeAnnotations; // the actual material we're managing here |
| final private long matches; // bit mask of indices into typeAnnotations, 1 means active, 0 is filtered during the walk |
| final private int pathPtr; // pointer into the typePath |
| |
| // precondition: not-empty typeAnnotations |
| public TypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations) { |
| this(typeAnnotations, -1L >>> (64-typeAnnotations.length)); // initialize so lowest length bits are 1 |
| } |
| TypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations, long matchBits) { |
| this(typeAnnotations, matchBits, 0); |
| } |
| private TypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations, long matchBits, int pathPtr) { |
| this.typeAnnotations = typeAnnotations; |
| this.matches = matchBits; |
| this.pathPtr = pathPtr; |
| } |
| |
| private TypeAnnotationWalker restrict(long newMatches, int newPathPtr) { |
| if (this.matches == newMatches && this.pathPtr == newPathPtr) return this; |
| if (newMatches == 0 || this.typeAnnotations == null || this.typeAnnotations.length == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| return new TypeAnnotationWalker(this.typeAnnotations, newMatches, newPathPtr); |
| } |
| |
| // ==== filter by top-level targetType: ==== |
| |
| /** Walk to a field. */ |
| public TypeAnnotationWalker toField() { |
| return toTarget(AnnotationTargetTypeConstants.FIELD); |
| } |
| |
| /** Walk to the return type of a method. */ |
| public TypeAnnotationWalker toMethodReturn() { |
| return toTarget(AnnotationTargetTypeConstants.METHOD_RETURN); |
| } |
| |
| /** |
| * Walk to the receiver type of a method. |
| * Note: Type annotations on receiver are not currently used by the compiler. |
| */ |
| public TypeAnnotationWalker toReceiver() { |
| return toTarget(AnnotationTargetTypeConstants.METHOD_RECEIVER); |
| } |
| |
| /* |
| * Implementation for walking to methodReturn, receiver type or field. |
| */ |
| protected TypeAnnotationWalker toTarget(int targetType) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| if (this.typeAnnotations[i].getTargetType() != targetType) |
| newMatches &= ~mask; |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| /** |
| * Walk to the type parameter of the given rank. |
| * @param isClassTypeParameter whether we are looking for a class type parameter (else: method type type parameter) |
| * @param rank rank of the type parameter |
| */ |
| public TypeAnnotationWalker toTypeParameter(boolean isClassTypeParameter, int rank) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int targetType = isClassTypeParameter ? AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER : AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTargetType() != targetType || candidate.getTypeParameterIndex() != rank) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| /** |
| * Walk to the bounds of a type parameter of either a class or a method (signaled by isClassTypeParameter). |
| * Clients must then call {@link #toTypeBound(short)} on the resulting walker. |
| * @param isClassTypeParameter whether we are looking at a class type parameter (else: method type type parameter) |
| * @param parameterRank rank of the type parameter. |
| */ |
| public TypeAnnotationWalker toTypeParameterBounds(boolean isClassTypeParameter, int parameterRank) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| int targetType = isClassTypeParameter ? |
| AnnotationTargetTypeConstants.CLASS_TYPE_PARAMETER_BOUND : AnnotationTargetTypeConstants.METHOD_TYPE_PARAMETER_BOUND; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTargetType() != targetType || (short)candidate.getTypeParameterIndex() != parameterRank) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| /** |
| * Detail of {@link #toTypeParameterBounds(boolean, int)}: walk to the bounds |
| * of the previously selected type parameter. |
| * @param boundIndex |
| */ |
| public TypeAnnotationWalker toTypeBound(short boundIndex) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if ((short)candidate.getBoundIndex() != boundIndex) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| |
| /** Walk to the specified supertype: -1 is superclass, else the superinterface at the given index. */ |
| public TypeAnnotationWalker toSupertype(short index) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTargetType() != AnnotationTargetTypeConstants.CLASS_EXTENDS || (short)candidate.getSupertypeIndex() != index) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| /** Walk to the index'th visible formal method parameter (i.e., not counting synthetic args). */ |
| public TypeAnnotationWalker toMethodParameter(short index) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTargetType() != AnnotationTargetTypeConstants.METHOD_FORMAL_PARAMETER || (short)candidate.getMethodFormalParameterIndex() != index) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| /** |
| * Walk to the throws type at the given index. |
| */ |
| public TypeAnnotationWalker toThrows(int index) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTargetType() != AnnotationTargetTypeConstants.THROWS || candidate.getThrowsTypeIndex() != index) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, 0); |
| } |
| |
| // ==== descending into details: ==== |
| |
| /** Walk to the type argument of the given rank. */ |
| public TypeAnnotationWalker toTypeArgument(int rank) { |
| // like toNextDetail() but also checking byte 2 against rank |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| int[] path = candidate.getTypePath(); |
| if (this.pathPtr >= path.length |
| || path[this.pathPtr] != AnnotationTargetTypeConstants.TYPE_ARGUMENT |
| || path[this.pathPtr+1] != rank) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, this.pathPtr+2); |
| } |
| |
| /** Walk to the bound of a wildcard. */ |
| public TypeAnnotationWalker toWildcardBound() { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| int[] path = candidate.getTypePath(); |
| if (this.pathPtr >= path.length |
| || path[this.pathPtr] != AnnotationTargetTypeConstants.WILDCARD_BOUND) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, this.pathPtr+2); |
| } |
| |
| /** |
| * Descend down one level of array dimensions. |
| */ |
| public TypeAnnotationWalker toNextArrayDimension() { |
| return toNextDetail(AnnotationTargetTypeConstants.NEXT_ARRAY_DIMENSION); |
| } |
| |
| /** |
| * Descend down one level of type nesting. |
| */ |
| public TypeAnnotationWalker toNextNestedType() { |
| return toNextDetail(AnnotationTargetTypeConstants.NEXT_NESTED_TYPE); |
| } |
| |
| /* |
| * Implementation for walking along the type_path for array dimensions & nested types. |
| * FIXME(stephan): support wildcard bounds. |
| */ |
| protected TypeAnnotationWalker toNextDetail(int detailKind) { |
| long newMatches = this.matches; |
| if (newMatches == 0) |
| return EMPTY_ANNOTATION_WALKER; |
| int length = this.typeAnnotations.length; |
| long mask = 1; |
| for (int i = 0; i < length; i++, mask = mask << 1) { |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| int[] path = candidate.getTypePath(); |
| if (this.pathPtr >= path.length || path[this.pathPtr] != detailKind) { |
| newMatches &= ~mask; |
| } |
| } |
| return restrict(newMatches, this.pathPtr+2); |
| } |
| |
| // ==== leaves: the actual annotations: ==== |
| |
| /** |
| * Retrieve the type annotations at the current position |
| * reached by invocations of toXYZ() methods. |
| */ |
| public IBinaryAnnotation[] getAnnotationsAtCursor() { |
| int length = this.typeAnnotations.length; |
| IBinaryAnnotation[] filtered = new IBinaryAnnotation[length]; |
| long ptr = 1; |
| int count = 0; |
| for (int i = 0; i < length; i++, ptr<<=1) { |
| if ((this.matches & ptr) == 0) |
| continue; |
| IBinaryTypeAnnotation candidate = this.typeAnnotations[i]; |
| if (candidate.getTypePath().length > this.pathPtr) |
| continue; |
| filtered[count++] = candidate.getAnnotation(); |
| } |
| if (count == 0) |
| return NO_ANNOTATIONS; |
| if (count < length) |
| System.arraycopy(filtered, 0, filtered = new IBinaryAnnotation[count], 0, count); |
| return filtered; |
| } |
| } |