Copyright (c) 2013, 2017 GK Software AG.
This program and the accompanying materials
are made available under the terms of the Eclipse Public License 2.0
which accompanies this distribution, and is available at
https://www.eclipse.org/legal/epl-2.0/
SPDX-License-Identifier: EPL-2.0
Contributors:
Stephan Herrmann - initial API and implementation
/*******************************************************************************
* Copyright (c) 2013, 2017 GK Software AG.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* 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. /** 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}
(superTypesSignature is ignored in this implementation).
/**
* {@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 restrict(newMatches, this.pathPtr+2);
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, boolean mayApplyArrayContentsDefaultNullness) {
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;
}
}