/******************************************************************************* * Copyright (c) 2013, 2015 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.eclipse.jdt.internal.compiler.classfmt; import org.eclipse.jdt.internal.compiler.codegen.AnnotationTargetTypeConstants; import org.eclipse.jdt.internal.compiler.env.IBinaryAnnotation; import org.eclipse.jdt.internal.compiler.env.IBinaryTypeAnnotation; import org.eclipse.jdt.internal.compiler.env.ITypeAnnotationWalker; /** Type annotation walker implementation based an actual annotations decoded from a .class file. */ public class TypeAnnotationWalker implements ITypeAnnotationWalker { final protected IBinaryTypeAnnotation[] typeAnnotations; // the actual material we're managing here final protected long matches; // bit mask of indices into typeAnnotations, 1 means active, 0 is filtered during the walk final protected 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); } protected TypeAnnotationWalker(IBinaryTypeAnnotation[] typeAnnotations, long matchBits, int pathPtr) { this.typeAnnotations = typeAnnotations; this.matches = matchBits; this.pathPtr = pathPtr; } protected ITypeAnnotationWalker 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: ==== @Override public ITypeAnnotationWalker toField() { return toTarget(AnnotationTargetTypeConstants.FIELD); } @Override public ITypeAnnotationWalker toMethodReturn() { return toTarget(AnnotationTargetTypeConstants.METHOD_RETURN); } @Override public ITypeAnnotationWalker toReceiver() { return toTarget(AnnotationTargetTypeConstants.METHOD_RECEIVER); } /* * Implementation for walking to methodReturn, receiver type or field. */ protected ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker 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); } /** * {@inheritDoc} * <p>(superTypesSignature is ignored in this implementation).</p> */ @Override public ITypeAnnotationWalker toSupertype(short index, char[] superTypeSignature) { 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); } @Override public ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker 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: ==== @Override public ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker 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); } @Override public ITypeAnnotationWalker toNextArrayDimension() { return toNextDetail(AnnotationTargetTypeConstants.NEXT_ARRAY_DIMENSION); } @Override public ITypeAnnotationWalker toNextNestedType() { return toNextDetail(AnnotationTargetTypeConstants.NEXT_NESTED_TYPE); } /* * Implementation for walking along the type_path for array dimensions & nested types. */ protected ITypeAnnotationWalker 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: ==== @Override public IBinaryAnnotation[] getAnnotationsAtCursor(int currentTypeId) { 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; } }