com.strobel.reflection.emit.Verifier Maven / Gradle / Ivy
/*
* Verifier.java
*
* Copyright (c) 2015 Mike Strobel
*
* This source code is subject to terms and conditions of the Apache License, Version 2.0.
* A copy of the license can be found in the License.html file at the root of this distribution.
* By using this source code in any fashion, you are agreeing to be bound by the terms of the
* Apache License, Version 2.0.
*
* You must not remove this notice, or any other, from this software.
*/
package com.strobel.reflection.emit;
import com.strobel.core.StringUtilities;
import com.strobel.reflection.*;
import com.strobel.util.ContractUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
import java.util.Stack;
import static java.lang.String.format;
final class Verifier {
private final static String VERIFY_LOCAL_VARIABLE_TYPES = "com.strobel.reflection.emit.Verifier.VerifyLocalVariableTypes";
private final static GenericParameterResolver GENERIC_PARAMETER_RESOLVER = new GenericParameterResolver();
private Verifier() {
}
public static void verify(final MethodBase method, final MethodBuilder scope) {
final GenericParameterScopeVerifier methodLevelVerifier = new GenericParameterScopeVerifier();
methodLevelVerifier.visit(method, scope);
}
public static void verify(final FieldInfo field, final MethodBuilder scope) {
final GenericParameterScopeVerifier methodLevelVerifier = new GenericParameterScopeVerifier();
methodLevelVerifier.visit(field, scope);
}
public static void verify(final TypeBuilder> type) {
final GenericParameterScopeVerifier typeLevelVerifier = new GenericParameterScopeVerifier();
typeLevelVerifier._frames.push(new VerifierFrame(FrameType.TYPE_SIGNATURE, type));
typeLevelVerifier._frames.push(new VerifierFrame(FrameType.SUPER_CLASS, type));
final Type> baseType = type.getBaseType();
if (baseType != null) {
typeLevelVerifier.visit(baseType, type);
}
typeLevelVerifier._frames.pop();
typeLevelVerifier.visit(type.getExplicitInterfaces(), type, FrameType.SUPER_INTERFACE);
if (type.isGenericTypeDefinition()) {
typeLevelVerifier.visit(type.getGenericTypeParameters(), type, FrameType.TYPE_VARIABLE);
}
else if (type.isGenericType()) {
typeLevelVerifier.visit(type.getTypeArguments(), type, FrameType.TYPE_ARGUMENT);
}
for (final FieldBuilder field : type.fieldBuilders) {
typeLevelVerifier.visit(field, type);
}
for (final ConstructorBuilder ctor : type.constructorBuilders) {
final GenericParameterScopeVerifier methodLevelVerifier = new GenericParameterScopeVerifier();
methodLevelVerifier._frames.addAll(typeLevelVerifier._frames);
methodLevelVerifier._visitedTypes.addAll(typeLevelVerifier._visitedTypes);
methodLevelVerifier.visit(ctor.getMethodBuilder(), ctor.getMethodBuilder());
}
for (final MethodBuilder method : type.methodBuilders) {
final GenericParameterScopeVerifier methodLevelVerifier = new GenericParameterScopeVerifier();
methodLevelVerifier._frames.addAll(typeLevelVerifier._frames);
methodLevelVerifier._visitedTypes.addAll(typeLevelVerifier._visitedTypes);
methodLevelVerifier.visit(method, method);
}
typeLevelVerifier._frames.pop();
}
private final static class GenericParameterScopeVerifier extends SimpleVisitor {
private final Stack _frames = new Stack<>();
private final Set> _visitedTypes = new LinkedHashSet<>();
public Void visit(final FieldInfo field, final MemberInfo scope) {
// field.getDeclaringType().accept(this, scope);
_frames.push(new VerifierFrame(FrameType.FIELD_SIGNATURE, field));
field.getFieldType().accept(this, scope);
_frames.pop();
return null;
}
public Void visit(final MethodBase method, final MemberInfo scope) {
// method.getDeclaringType().accept(this, scope);
_frames.push(new VerifierFrame(FrameType.METHOD_SIGNATURE, method));
if (method instanceof MethodInfo) {
final MethodInfo m = (MethodInfo) method;
_frames.push(new VerifierFrame(FrameType.METHOD_RETURN_TYPE, method));
m.getReturnType().accept(this, scope);
_frames.pop();
if (m.isGenericMethodDefinition()) {
visit(m.getGenericMethodParameters(), scope, FrameType.TYPE_VARIABLE);
}
else if (m.isGenericMethod()) {
visit(m.getTypeArguments(), scope, FrameType.TYPE_ARGUMENT);
}
}
visit(method.getParameters().getParameterTypes(), scope, FrameType.METHOD_PARAMETER);
visit(method.getThrownTypes(), scope, FrameType.METHOD_THROWS_LIST);
if (StringUtilities.isTrue(System.getProperty(VERIFY_LOCAL_VARIABLE_TYPES))) {
final MethodBuilder mb;
if (method instanceof MethodBuilder) {
mb = (MethodBuilder) method;
}
else if (method instanceof ConstructorBuilder) {
mb = ((ConstructorBuilder) method).getMethodBuilder();
}
else {
mb = null;
}
if (mb != null && mb.generator != null) {
for (int i = 0; i < mb.generator.localCount; i++) {
final LocalBuilder local = mb.generator.locals[i];
_frames.push(new VerifierFrame(FrameType.LOCAL_VARIABLE, local.getLocalType(), local));
local.getLocalType().accept(this, scope);
_frames.pop();
}
}
}
_frames.pop();
return null;
}
@Override
public Void visitTypeParameter(final Type> type, final MemberInfo scope) {
super.visitTypeParameter(type, scope);
if (type.hasSuperBound()) {
_frames.push(new VerifierFrame(FrameType.TYPE_BOUND, type.getSuperBound()));
visit(type.getSuperBound(), scope);
_frames.pop();
}
else if (type.hasExtendsBound() && !Types.Object.isEquivalentTo(type.getExtendsBound())) {
_frames.push(new VerifierFrame(FrameType.TYPE_BOUND, type.getExtendsBound()));
visit(type.getExtendsBound(), scope);
_frames.pop();
}
if (!type.isGenericParameter() || !_visitedTypes.add(type)) {
return null;
}
if (GENERIC_PARAMETER_RESOLVER.visitScope(scope, type) != Boolean.TRUE) {
final ArrayList frames = new ArrayList<>(_frames);
Collections.reverse(frames);
throw new VerificationException(
typeVariableOutOfScopeError(type, scope),
frames.toArray(VerifierFrame.EMPTY_FRAMES)
);
}
return null;
}
public Void visit(final TypeList types, final MemberInfo scope, final FrameType frameType) {
for (final Type> type : types) {
_frames.push(new VerifierFrame(frameType, type));
type.accept(this, scope);
_frames.pop();
}
return null;
}
@Override
public Void visitArrayType(final Type> type, final MemberInfo scope) {
super.visitArrayType(type, scope);
_frames.push(new VerifierFrame(FrameType.TYPE_SIGNATURE, type));
type.getElementType().accept(this, scope);
_frames.pop();
return null;
}
@Override
public Void visitClassType(final Type> type, final MemberInfo scope) {
super.visitClassType(type, scope);
_frames.push(new VerifierFrame(FrameType.TYPE_SIGNATURE, type));
if (type.isGenericTypeDefinition()) {
visit(type.getGenericTypeParameters(), scope, FrameType.TYPE_VARIABLE);
}
else if (type.isGenericType()) {
visit(type.getTypeArguments(), scope, FrameType.TYPE_ARGUMENT);
}
_frames.pop();
return null;
}
@Override
public Void visitPrimitiveType(final Type> type, final MemberInfo scope) {
return null;
}
@Override
public Void visitWildcardType(final Type> type, final MemberInfo scope) {
super.visitWildcardType(type, scope);
if (type.isUnbounded()) {
return null;
}
if (type.hasSuperBound()) {
_frames.push(new VerifierFrame(FrameType.TYPE_BOUND, type.getSuperBound()));
visit(type.getSuperBound(), scope);
_frames.pop();
}
else {
_frames.push(new VerifierFrame(FrameType.TYPE_BOUND, type.getExtendsBound()));
visit(type.getExtendsBound(), scope);
_frames.pop();
}
return null;
}
@Override
public Void visitCapturedType(final Type> type, final MemberInfo scope) {
super.visitCapturedType(type, scope);
if (type instanceof ICapturedType) {
visit(((ICapturedType) type).getWildcard(), scope);
}
return null;
}
}
static String typeVariableOutOfScopeError(final Type> typeParameter, final MemberInfo scope) {
final String site;
final String owner;
final MethodBase declaringMethod = typeParameter.getDeclaringMethod();
final Type> declaringType = declaringMethod != null ? declaringMethod.getDeclaringType()
: typeParameter.getDeclaringType();
if (declaringMethod != null) {
owner = format(
"method '%s' on type '%s'",
declaringMethod.getSimpleDescription(),
declaringType.getSimpleDescription()
);
}
else {
owner = "type '" + declaringType.getSimpleDescription() + "'";
}
if (scope instanceof MethodBase) {
site = format(
"method '%s' on type '%s'",
scope.getSimpleDescription(),
scope.getDeclaringType().getSimpleDescription()
);
}
else {
site = "type '" + scope.getSimpleDescription() + "'";
}
return format(
"Type variable '%s' cannot be resolved in %s (variable owned by %s).",
typeParameter.getName(),
site,
owner
);
}
private final static class GenericParameterResolver extends SimpleVisitor, Boolean> {
public Boolean visitScope(final MemberInfo scope, final Type> s) {
if (scope instanceof MethodBase) {
final MethodBase method = (MethodBase) scope;
if (method.containsGenericParameter(s)) {
return Boolean.TRUE;
}
final Type> declaringType = method.getDeclaringType();
if (declaringType != null && !declaringType.isStatic()) {
return declaringType.accept(this, s);
}
return Boolean.FALSE;
}
if (scope instanceof Type>) {
final Type> type = (Type>) scope;
if (type.containsGenericParameter(s)) {
return Boolean.TRUE;
}
if (!type.isStatic()) {
final Type> declaringType = type.getDeclaringType();
if (declaringType != null) {
return declaringType.accept(this, s);
}
return Boolean.FALSE;
}
}
return Boolean.FALSE;
}
public Boolean visitTypes(final TypeList types, final Type> s) {
for (final Type> type : types) {
if (visit(type, s) == Boolean.TRUE) {
return Boolean.TRUE;
}
}
return Boolean.FALSE;
}
@Override
public Boolean visitCapturedType(final Type> t, final Type> s) {
return Boolean.FALSE;
}
@Override
public Boolean visitClassType(final Type> type, final Type> s) {
if (type.containsGenericParameter(s)) {
return Boolean.TRUE;
}
final MethodBase declaringMethod = type.getDeclaringMethod();
if (declaringMethod != null) {
return visitScope(declaringMethod, s);
}
if (type.isStatic()) {
return Boolean.FALSE;
}
final Type> declaringType = type.getDeclaringType();
if (declaringType != null && declaringType != Type.nullType()) {
return visit(declaringType, s);
}
return Boolean.FALSE;
}
@Override
public Boolean visitPrimitiveType(final Type> type, final Type> s) {
return Boolean.FALSE;
}
@Override
public Boolean visitTypeParameter(final Type> type, final Type> s) {
if (type.isEquivalentTo(s)) {
return Boolean.TRUE;
}
return Boolean.FALSE;
}
@Override
public Boolean visitWildcardType(final Type> type, final Type> s) {
return Boolean.FALSE;
}
@Override
public Boolean visitArrayType(final Type> type, final Type> s) {
return Boolean.FALSE;
}
}
enum FrameType {
FIELD_SIGNATURE,
METHOD_SIGNATURE,
METHOD_RETURN_TYPE,
METHOD_PARAMETER,
METHOD_THROWS_LIST,
METHOD_ARGUMENT,
TYPE_SIGNATURE,
TYPE_VARIABLE,
LOCAL_VARIABLE,
TYPE_ARGUMENT,
TYPE_BOUND,
SUPER_CLASS,
SUPER_INTERFACE,
}
final static class VerifierFrame {
private final static VerifierFrame[] EMPTY_FRAMES = new VerifierFrame[0];
private final FrameType _frameType;
private final MemberInfo _member;
private final Object _location;
public VerifierFrame(final FrameType frameType, final MemberInfo member) {
this(frameType, member, null);
}
public VerifierFrame(final FrameType frameType, final MemberInfo member, final Object location) {
_frameType = frameType;
_member = member;
_location = location;
}
@Override
public String toString() {
switch (_frameType) {
case FIELD_SIGNATURE: {
return "field: " + _member.getDescription();
}
case METHOD_SIGNATURE: {
return "method: " + _member.getDescription();
}
case METHOD_RETURN_TYPE: {
return "return type: " + _member.getBriefDescription();
}
case METHOD_THROWS_LIST: {
return "throws list: " + _member.getBriefDescription();
}
case METHOD_PARAMETER: {
if (_location instanceof ParameterInfo) {
final ParameterInfo p = (ParameterInfo) _location;
if (StringUtilities.isNullOrEmpty(p.getName())) {
return "parameter #" + p.getPosition() +
": " + _member.getSignature();
}
return "parameter #" + p.getPosition() +
" (" + p.getName() + "): " + _member.getSignature();
}
return "parameter: " + _member.getSignature();
}
case METHOD_ARGUMENT: {
if (_location instanceof ParameterInfo) {
final ParameterInfo p = (ParameterInfo) _location;
if (StringUtilities.isNullOrEmpty(p.getName())) {
return "argument #" + p.getPosition() +
": " + _member.getBriefDescription();
}
return "argument #" + p.getPosition() +
" (" + p.getName() + "): " + _member.getBriefDescription();
}
return "argument: " + _member.getBriefDescription();
}
case TYPE_SIGNATURE: {
return "type: " + _member.getDescription();
}
case TYPE_VARIABLE: {
return "type variable: " + _member.getDescription();
}
case TYPE_BOUND: {
return "type bound: " + _member.getDescription();
}
case SUPER_CLASS: {
return "super class: " + _member.getDescription();
}
case SUPER_INTERFACE: {
return "super interface: " + _member.getDescription();
}
case TYPE_ARGUMENT: {
if (_location instanceof Type> &&
((Type>) _location).isGenericParameter()) {
final Type> gp = (Type>) _location;
return "type argument #" + gp.getGenericParameterPosition() +
" (" + gp.getName() + "): " + _member.getBriefDescription();
}
return "type argument: " + _member.getBriefDescription();
}
case LOCAL_VARIABLE: {
if (_location instanceof LocalVariableInfo) {
final LocalVariableInfo local = (LocalVariableInfo) _location;
if (local instanceof LocalBuilder) {
return "local variable #" + local.getLocalIndex() +
" (" + ((LocalBuilder) local).getName() + "): " +
local.getLocalType().getBriefDescription();
}
return "local variable #" + local.getLocalIndex() +
": " + local.getLocalType().getBriefDescription();
}
return "local variable in method " + _member.getName();
}
default: {
throw ContractUtils.unreachable();
}
}
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy