private JavaSymbol findMemberType( Env env, JavaSymbol.TypeJavaSymbol site, String name, JavaSymbol.TypeJavaSymbol c) { JavaSymbol bestSoFar = symbolNotFound; for (JavaSymbol symbol : c.members().lookup(name)) { if (symbol.kind == JavaSymbol.TYP) { return isAccessible(env, site, symbol) ? symbol : new AccessErrorJavaSymbol(symbol, Symbols.unknownType); } } if (c.getSuperclass() != null) { JavaSymbol symbol = findMemberType(env, site, name, c.getSuperclass().symbol); if (symbol.kind < bestSoFar.kind) { bestSoFar = symbol; } } if (c.getInterfaces() == null) { // Invariant to check that interfaces are not set only when we are looking into the symbol we // are currently completing. // required for generics Preconditions.checkState( c.completing, "interfaces of a symbol not currently completing are not set."); Preconditions.checkState(c == site); } else { for (JavaType interfaceType : c.getInterfaces()) { JavaSymbol symbol = findMemberType(env, site, name, interfaceType.symbol); if (symbol.kind < bestSoFar.kind) { bestSoFar = symbol; } } } return bestSoFar; }
/** Is symbol inherited in given class? */ @VisibleForTesting static boolean isInheritedIn(JavaSymbol symbol, JavaSymbol.TypeJavaSymbol clazz) { switch (symbol.flags() & Flags.ACCESS_FLAGS) { case Flags.PUBLIC: return true; case Flags.PRIVATE: return symbol.owner() == clazz; case Flags.PROTECTED: // TODO see Javac return true; case 0: // TODO see Javac JavaSymbol.PackageJavaSymbol thisPackage = symbol.packge(); for (JavaSymbol.TypeJavaSymbol sup = clazz; sup != null && sup != symbol.owner(); sup = superclassSymbol(sup)) { if (sup.packge() != thisPackage) { return false; } } return true; default: throw new IllegalStateException(); } }
/** Finds field with given name. */ private Resolution findField( Env env, JavaSymbol.TypeJavaSymbol site, String name, JavaSymbol.TypeJavaSymbol c) { Resolution bestSoFar = unresolved(); Resolution resolution = new Resolution(); for (JavaSymbol symbol : c.members().lookup(name)) { if (symbol.kind == JavaSymbol.VAR) { if (isAccessible(env, site, symbol)) { resolution.symbol = symbol; resolution.type = resolveTypeSubstitution(symbol.type, c.type); return resolution; } else { return Resolution.resolution(new AccessErrorJavaSymbol(symbol, Symbols.unknownType)); } } } if (c.getSuperclass() != null) { resolution = findField(env, site, name, c.getSuperclass().symbol); if (resolution.symbol.kind < bestSoFar.symbol.kind) { resolution.type = resolveTypeSubstitution(resolution.symbol.type, c.getSuperclass()); bestSoFar = resolution; } } for (JavaType interfaceType : c.getInterfaces()) { resolution = findField(env, site, name, interfaceType.symbol); if (resolution.symbol.kind < bestSoFar.symbol.kind) { bestSoFar = resolution; } } return bestSoFar; }
/** Registers builtin types as symbols, so that they can be found as an usual identifiers. */ private JavaType initType(int tag, String name) { JavaSymbol.TypeJavaSymbol symbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, name, rootPackage); symbol.members = new Scope(symbol); predefClass.members.enter(symbol); ((JavaType.ClassJavaType) symbol.type).interfaces = ImmutableList.of(); symbol.type.tag = tag; return symbol.type; }
/** * Invoked when current class classified as inner class. Owner of inner classes - is some outer * class, which is either already completed, and thus already has this inner class as member, * either will be completed by {@link org.sonar.java.resolve.BytecodeCompleter}, and thus will * have this inner class as member (see {@link #defineInnerClass(String, int)}). */ private void defineOuterClass(String outerName, String innerName, int flags) { JavaSymbol.TypeJavaSymbol outerClassSymbol = getClassSymbol(outerName, flags); Preconditions.checkState( outerClassSymbol.completer == null || outerClassSymbol.completer instanceof BytecodeCompleter); classSymbol.name = innerName; classSymbol.flags = flags | bytecodeCompleter.filterBytecodeFlags(classSymbol.flags & ~Flags.ACCESS_FLAGS); classSymbol.owner = outerClassSymbol; }
/** * Invoked when current class classified as outer class of some inner class. Adds inner class as * member. */ private void defineInnerClass(String bytecodeName, int flags) { JavaSymbol.TypeJavaSymbol innerClass = getClassSymbol(classSymbol, bytecodeName, flags); innerClass.flags |= bytecodeCompleter.filterBytecodeFlags(flags); Preconditions.checkState( innerClass.owner == classSymbol, "Innerclass: " + innerClass.owner.getName() + " and classSymbol: " + classSymbol.getName() + " are not the same."); classSymbol.members.enter(innerClass); }
/** * If at this point there is no owner of current class, then this is a top-level class, because * outer classes always will be completed before inner classes - see {@link * #defineOuterClass(String, String, int)}. Owner of top-level classes - is a package. */ @Override public void visitEnd() { if (classSymbol.owner == null) { String flatName = className.replace('/', '.'); classSymbol.name = flatName.substring(flatName.lastIndexOf('.') + 1); classSymbol.owner = bytecodeCompleter.enterPackage(flatName); JavaSymbol.PackageJavaSymbol owner = (JavaSymbol.PackageJavaSymbol) classSymbol.owner; if (owner.members == null) { // package was without classes so far owner.members = new Scope(owner); } owner.members.enter(classSymbol); } }
@Override public void visitFormalTypeParameter(String name) { JavaSymbol.TypeVariableJavaSymbol typeVariableSymbol = new JavaSymbol.TypeVariableJavaSymbol(name, symbol); ((TypeVariableJavaType) typeVariableSymbol.type).bounds = Lists.newArrayList(); if (symbol.isTypeSymbol()) { JavaSymbol.TypeJavaSymbol typeJavaSymbol = (JavaSymbol.TypeJavaSymbol) symbol; typeJavaSymbol.typeParameters.enter(typeVariableSymbol); typeJavaSymbol.addTypeParameter((TypeVariableJavaType) typeVariableSymbol.type); } else if (symbol.isMethodSymbol()) { JavaSymbol.MethodJavaSymbol methodSymbol = (JavaSymbol.MethodJavaSymbol) symbol; methodSymbol.typeParameters.enter(typeVariableSymbol); methodSymbol.addTypeParameter((TypeVariableJavaType) typeVariableSymbol.type); } }
@Override public AnnotationVisitor visitAnnotation(String desc, boolean visible) { JavaType annotationType = convertAsmType(org.objectweb.asm.Type.getType(desc)); AnnotationInstanceResolve annotationInstance = new AnnotationInstanceResolve(annotationType.getSymbol()); classSymbol.metadata().addAnnotation(annotationInstance); return new BytecodeAnnotationVisitor(annotationInstance, this); }
@Override public void visitEnd() { if (!typeArguments.isEmpty()) { JavaSymbol.TypeJavaSymbol readSymbol = typeRead.symbol; readSymbol.complete(); // Mismatch between type variable and type arguments means we are lacking some pieces of // bytecode to resolve substitution properly. if (typeArguments.size() == readSymbol.typeVariableTypes.size()) { TypeSubstitution substitution = new TypeSubstitution(); int i = 0; for (JavaType typeArgument : typeArguments) { substitution.add(readSymbol.typeVariableTypes.get(i), typeArgument); i++; } typeRead = parametrizedTypeCache.getParametrizedTypeType(readSymbol, substitution); } } }
@Override public void visit( int version, int flags, String name, @Nullable String signature, @Nullable String superName, @Nullable String[] interfaces) { Preconditions.checkState( name.endsWith(classSymbol.name), "Name : '" + name + "' should ends with " + classSymbol.name); Preconditions.checkState(!BytecodeCompleter.isSynthetic(flags), name + " is synthetic"); className = name; if (signature != null) { SignatureReader signatureReader = new SignatureReader(signature); signatureReader.accept(new TypeParameterDeclaration(classSymbol)); ReadGenericSignature readGenericSignature = new ReadGenericSignature(); signatureReader.accept(readGenericSignature); ((ClassJavaType) classSymbol.type).interfaces = readGenericSignature.interfaces(); } else { if (superName == null) { Preconditions.checkState( "java/lang/Object".equals(className), "superName must be null only for java/lang/Object, but not for " + className); // TODO(Godin): what about interfaces and annotations } else { ((ClassJavaType) classSymbol.type).supertype = getClassSymbol(superName).type; } ((ClassJavaType) classSymbol.type).interfaces = getCompletedClassSymbolsType(interfaces); } // if class has already access flags set (inner class) then do not reset those. // The important access flags are the one defined in the outer class. if ((classSymbol.flags & Flags.ACCESS_FLAGS) != 0) { classSymbol.flags |= bytecodeCompleter.filterBytecodeFlags(flags & ~Flags.ACCESS_FLAGS); } else { classSymbol.flags |= bytecodeCompleter.filterBytecodeFlags(flags); } classSymbol.members = new Scope(classSymbol); }
/** Is class accessible in given environment? */ @VisibleForTesting static boolean isAccessible(Env env, JavaSymbol.TypeJavaSymbol c) { final boolean result; switch (c.flags() & Flags.ACCESS_FLAGS) { case Flags.PRIVATE: result = sameOutermostClass(env.enclosingClass, c.owner()); break; case 0: result = env.packge == c.packge(); break; case Flags.PUBLIC: result = true; break; case Flags.PROTECTED: result = env.packge == c.packge() || isInnerSubClass(env.enclosingClass, c.owner()); break; default: throw new IllegalStateException(); } // TODO check accessibility of enclosing type: isAccessible(env, c.type.getEnclosingType()) return result; }
static { rootPackage = new JavaSymbol.PackageJavaSymbol("", null); unknownSymbol = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "!unknownSymbol!", rootPackage) { @Override public void addUsage(IdentifierTree tree) { // noop } @Override public boolean isTypeSymbol() { return false; } @Override public boolean isUnknown() { return true; } }; unknownSymbol.members = new Scope(unknownSymbol) { @Override public void enter(JavaSymbol symbol) { // noop } }; unknownType = new JavaType.ClassJavaType(unknownSymbol) { @Override public String toString() { return "!unknown!"; } }; unknownType.tag = JavaType.UNKNOWN; unknownType.supertype = null; unknownType.interfaces = ImmutableList.of(); unknownSymbol.type = unknownType; }
/** Is given class a subclass of given base class? */ @VisibleForTesting static boolean isSubClass(JavaSymbol.TypeJavaSymbol c, JavaSymbol base) { // TODO get rid of null check if (c == null) { return false; } // TODO see Javac if (c == base) { // same class return true; } else if ((base.flags() & Flags.INTERFACE) != 0) { // check if class implements base for (JavaType interfaceType : c.getInterfaces()) { if (isSubClass(interfaceType.symbol, base)) { return true; } } // check if superclass implements base return isSubClass(superclassSymbol(c), base); } else { // check if class extends base or its superclass extends base return isSubClass(superclassSymbol(c), base); } }
@Nullable private static JavaSymbol.TypeJavaSymbol superclassSymbol(JavaSymbol.TypeJavaSymbol c) { JavaType supertype = c.getSuperclass(); return supertype == null ? null : supertype.symbol; }
public Symbols(BytecodeCompleter bytecodeCompleter) { defaultPackage = new JavaSymbol.PackageJavaSymbol("", rootPackage); predefClass = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "", rootPackage); predefClass.members = new Scope(predefClass); ((JavaType.ClassJavaType) predefClass.type).interfaces = ImmutableList.of(); // TODO should have type "noType": noSymbol = new JavaSymbol.TypeJavaSymbol(0, "", rootPackage); methodClass = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "", noSymbol); // builtin types byteType = initType(JavaType.BYTE, "byte"); charType = initType(JavaType.CHAR, "char"); shortType = initType(JavaType.SHORT, "short"); intType = initType(JavaType.INT, "int"); longType = initType(JavaType.LONG, "long"); floatType = initType(JavaType.FLOAT, "float"); doubleType = initType(JavaType.DOUBLE, "double"); booleanType = initType(JavaType.BOOLEAN, "boolean"); nullType = initType(JavaType.BOT, "<nulltype>"); voidType = initType(JavaType.VOID, "void"); bytecodeCompleter.init(this); // predefined types for java lang JavaSymbol.PackageJavaSymbol javalang = bytecodeCompleter.enterPackage("java.lang"); // define a star import scope to let resolve types to java.lang when needed. javalang.members = new Scope.StarImportScope(javalang, bytecodeCompleter); javalang.members.enter(javalang); objectType = bytecodeCompleter.loadClass("java.lang.Object").type; classType = bytecodeCompleter.loadClass("java.lang.Class").type; stringType = bytecodeCompleter.loadClass("java.lang.String").type; cloneableType = bytecodeCompleter.loadClass("java.lang.Cloneable").type; serializableType = bytecodeCompleter.loadClass("java.io.Serializable").type; annotationType = bytecodeCompleter.loadClass("java.lang.annotation.Annotation").type; enumType = bytecodeCompleter.loadClass("java.lang.Enum").type; // Associate boxed types boxedTypes = HashBiMap.create(); boxedTypes.put(byteType, bytecodeCompleter.loadClass("java.lang.Byte").type); boxedTypes.put(charType, bytecodeCompleter.loadClass("java.lang.Character").type); boxedTypes.put(shortType, bytecodeCompleter.loadClass("java.lang.Short").type); boxedTypes.put(intType, bytecodeCompleter.loadClass("java.lang.Integer").type); boxedTypes.put(longType, bytecodeCompleter.loadClass("java.lang.Long").type); boxedTypes.put(floatType, bytecodeCompleter.loadClass("java.lang.Float").type); boxedTypes.put(doubleType, bytecodeCompleter.loadClass("java.lang.Double").type); boxedTypes.put(booleanType, bytecodeCompleter.loadClass("java.lang.Boolean").type); for (Entry<JavaType, JavaType> entry : boxedTypes.entrySet()) { entry.getKey().primitiveWrapperType = entry.getValue(); entry.getValue().primitiveType = entry.getKey(); } // TODO comment me arrayClass = new JavaSymbol.TypeJavaSymbol(Flags.PUBLIC, "Array", noSymbol); JavaType.ClassJavaType arrayClassType = (JavaType.ClassJavaType) arrayClass.type; arrayClassType.supertype = objectType; arrayClassType.interfaces = ImmutableList.of(cloneableType, serializableType); arrayClass.members = new Scope(arrayClass); arrayClass .members() .enter( new JavaSymbol.VariableJavaSymbol( Flags.PUBLIC | Flags.FINAL, "length", intType, arrayClass)); // TODO arrayClass implements clone() method enterOperators(); }
/** Is given class a subclass of given base class, or an inner class of a subclass? */ private static boolean isInnerSubClass(JavaSymbol.TypeJavaSymbol c, JavaSymbol base) { while (c != null && isSubClass(c, base)) { c = c.owner().enclosingClass(); } return c != null; }