diff options
author | Colin Cross <ccross@android.com> | 2019-01-24 10:23:34 -0800 |
---|---|---|
committer | android-build-merger <android-build-merger@google.com> | 2019-01-24 10:23:34 -0800 |
commit | 6d5f9e3e611533de9397480f7b84277f47e6189b (patch) | |
tree | fc48c84a601aa4170462d011afff6fb597364019 | |
parent | 2de126d9865445231da41e7d097d57b1d69eeb5e (diff) | |
parent | 3fec25b90823d67df97620ac55b1c596bc8c993a (diff) | |
download | turbine-6d5f9e3e611533de9397480f7b84277f47e6189b.tar.gz |
Merge remote-tracking branch 'aosp/upstream-master' into master
am: 3fec25b908
Change-Id: I1870c5f8c4e6fe78bf183698edd786c7c4278904
82 files changed, 2744 insertions, 1153 deletions
diff --git a/.travis.yml b/.travis.yml index 273556e..3ea8885 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,29 +1,27 @@ language: java +jdk: + - oraclejdk8 + - oraclejdk9 + - openjdk10 + - openjdk11 + - openjdk-ea + matrix: allow_failures: - - jdk: oraclejdk9 - include: -# JDK 8 - - jdk: oraclejdk8 - env: JDK_RELEASE='8' -# JDK 9 - - jdk: oraclejdk9 - env: JDK_RELEASE='9' + - jdk: openjdk-ea + +# see https://github.com/travis-ci/travis-ci/issues/8408 +before_install: +- unset _JAVA_OPTIONS # use travis-ci docker based infrastructure sudo: false -# https://github.com/travis-ci/travis-ci/issues/3259#issuecomment-130860338 -addons: - apt: - packages: - - oracle-java8-installer - cache: directories: - $HOME/.m2 -install: mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V - -script: mvn test -B +script: +- mvn install -DskipTests=true -Dmaven.javadoc.skip=true -B -V +- mvn test -B diff --git a/appveyor.yml b/appveyor.yml index bcbf5dc..44ff736 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,5 +1,10 @@ os: Visual Studio 2015 +environment: + matrix: + - JAVA_HOME: C:\Program Files\Java\jdk9 + - JAVA_HOME: C:\Program Files\Java\jdk10 + install: - ps: | Add-Type -AssemblyName System.IO.Compression.FileSystem diff --git a/java/com/google/turbine/binder/Binder.java b/java/com/google/turbine/binder/Binder.java index d4b151f..cffe291 100644 --- a/java/com/google/turbine/binder/Binder.java +++ b/java/com/google/turbine/binder/Binder.java @@ -16,8 +16,6 @@ package com.google.turbine.binder; - -import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; @@ -55,6 +53,7 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.ModuleSymbol; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; +import com.google.turbine.diag.TurbineLog; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineFlag; import com.google.turbine.tree.Tree; @@ -62,6 +61,7 @@ import com.google.turbine.tree.Tree.CompUnit; import com.google.turbine.tree.Tree.ModDecl; import com.google.turbine.type.Type; import java.util.List; +import java.util.Optional; /** The entry point for analysis. */ public class Binder { @@ -91,17 +91,24 @@ public class Binder { CompoundEnv<ModuleSymbol, ModuleInfo> classPathModuleEnv = CompoundEnv.of(classpath.moduleEnv()).append(bootclasspath.moduleEnv()); + TurbineLog log = new TurbineLog(); + BindPackagesResult bindPackagesResult = - bindPackages(ienv, tli, preProcessedUnits, classPathEnv); + bindPackages(log, ienv, tli, preProcessedUnits, classPathEnv); SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv = bindPackagesResult.classes; SimpleEnv<ModuleSymbol, PackageSourceBoundModule> modules = bindPackagesResult.modules; - Env<ClassSymbol, SourceHeaderBoundClass> henv = bindHierarchy(syms, psenv, classPathEnv); + Env<ClassSymbol, SourceHeaderBoundClass> henv = bindHierarchy(log, syms, psenv, classPathEnv); Env<ClassSymbol, SourceTypeBoundClass> tenv = bindTypes( - syms, henv, CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv)); + log, + syms, + henv, + CompoundEnv.<ClassSymbol, HeaderBoundClass>of(classPathEnv).append(henv)); + + log.maybeThrow(); tenv = constants( @@ -157,6 +164,7 @@ public class Binder { /** Initializes scopes for compilation unit and package-level lookup. */ private static BindPackagesResult bindPackages( + TurbineLog log, Env<ClassSymbol, SourceBoundClass> ienv, TopLevelIndex tli, ImmutableList<PreprocessedCompUnit> units, @@ -177,9 +185,10 @@ public class Binder { Scope packageScope = tli.lookupPackage(packagename); CanonicalSymbolResolver importResolver = new CanonicalResolver( - packagename, CompoundEnv.<ClassSymbol, BoundClass>of(classPathEnv).append(ienv)); + unit.packageName(), + CompoundEnv.<ClassSymbol, BoundClass>of(classPathEnv).append(ienv)); ImportScope importScope = - ImportIndex.create(unit.source(), importResolver, tli, unit.imports()); + ImportIndex.create(log.withSource(unit.source()), importResolver, tli, unit.imports()); ImportScope wildImportScope = WildImportIndex.create(importResolver, tli, unit.imports()); MemberImportIndex memberImports = new MemberImportIndex(unit.source(), importResolver, tli, unit.imports()); @@ -203,6 +212,7 @@ public class Binder { /** Binds the type hierarchy (superclasses and interfaces) for all classes in the compilation. */ private static Env<ClassSymbol, SourceHeaderBoundClass> bindHierarchy( + TurbineLog log, Iterable<ClassSymbol> syms, final SimpleEnv<ClassSymbol, PackageSourceBoundClass> psenv, CompoundEnv<ClassSymbol, BytecodeBoundClass> classPathEnv) { @@ -216,7 +226,8 @@ public class Binder { @Override public SourceHeaderBoundClass complete( Env<ClassSymbol, HeaderBoundClass> henv, ClassSymbol sym) { - return HierarchyBinder.bind(sym, psenv.get(sym), henv); + PackageSourceBoundClass base = psenv.get(sym); + return HierarchyBinder.bind(log.withSource(base.source()), sym, base, henv); } }); } @@ -224,12 +235,14 @@ public class Binder { } private static Env<ClassSymbol, SourceTypeBoundClass> bindTypes( + TurbineLog log, ImmutableSet<ClassSymbol> syms, Env<ClassSymbol, SourceHeaderBoundClass> shenv, Env<ClassSymbol, HeaderBoundClass> henv) { SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> builder = SimpleEnv.builder(); for (ClassSymbol sym : syms) { - builder.put(sym, TypeBinder.bind(henv, sym, shenv.get(sym))); + SourceHeaderBoundClass base = shenv.get(sym); + builder.put(sym, TypeBinder.bind(log.withSource(base.source()), henv, sym, base)); } return builder.build(); } @@ -257,20 +270,18 @@ public class Binder { new Env<ModuleSymbol, ModuleInfo>() { @Override public ModuleInfo get(ModuleSymbol sym) { - if (modules.asMap().containsKey(sym)) { - PackageSourceBoundModule info = modules.get(sym); - if (info != null) { - return new ModuleInfo( - info.module().moduleName(), - moduleVersion.orNull(), - /* flags= */ 0, - /* annos= */ ImmutableList.of(), - /* requires= */ ImmutableList.of(), - /* exports= */ ImmutableList.of(), - /* opens= */ ImmutableList.of(), - /* uses= */ ImmutableList.of(), - /* provides= */ ImmutableList.of()); - } + PackageSourceBoundModule info = modules.get(sym); + if (info != null) { + return new ModuleInfo( + info.module().moduleName(), + moduleVersion.orElse(null), + /* flags= */ 0, + /* annos= */ ImmutableList.of(), + /* requires= */ ImmutableList.of(), + /* exports= */ ImmutableList.of(), + /* opens= */ ImmutableList.of(), + /* uses= */ ImmutableList.of(), + /* provides= */ ImmutableList.of()); } return null; } diff --git a/java/com/google/turbine/binder/CanonicalTypeBinder.java b/java/com/google/turbine/binder/CanonicalTypeBinder.java index 20db0d7..934ec54 100644 --- a/java/com/google/turbine/binder/CanonicalTypeBinder.java +++ b/java/com/google/turbine/binder/CanonicalTypeBinder.java @@ -30,6 +30,8 @@ import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.SourceFile; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.IntersectionTy; +import com.google.turbine.type.Type.TyKind; import com.google.turbine.types.Canonicalize; import java.util.Map; @@ -41,17 +43,28 @@ public class CanonicalTypeBinder { static SourceTypeBoundClass bind( ClassSymbol sym, SourceTypeBoundClass base, Env<ClassSymbol, TypeBoundClass> env) { ClassTy superClassType = null; - if (base.superClassType() != null) { + if (base.superClassType() != null && base.superClassType().tyKind() == TyKind.CLASS_TY) { superClassType = - Canonicalize.canonicalizeClassTy(base.source(), env, base.owner(), base.superClassType()); + Canonicalize.canonicalizeClassTy( + base.source(), + base.decl().position(), + env, + base.owner(), + (ClassTy) base.superClassType()); } - ImmutableList.Builder<ClassTy> interfaceTypes = ImmutableList.builder(); - for (ClassTy i : base.interfaceTypes()) { - interfaceTypes.add(Canonicalize.canonicalizeClassTy(base.source(), env, base.owner(), i)); + ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder(); + for (Type i : base.interfaceTypes()) { + if (i.tyKind() == TyKind.CLASS_TY) { + i = + Canonicalize.canonicalizeClassTy( + base.source(), base.decl().position(), env, base.owner(), (ClassTy) i); + } + interfaceTypes.add(i); } ImmutableMap<TyVarSymbol, TyVarInfo> typParamTypes = - typeParameters(base.source(), env, sym, base.typeParameterTypes()); - ImmutableList<MethodInfo> methods = methods(base.source(), env, sym, base.methods()); + typeParameters(base.source(), base.decl().position(), env, sym, base.typeParameterTypes()); + ImmutableList<MethodInfo> methods = + methods(base.source(), base.decl().position(), env, sym, base.methods()); ImmutableList<FieldInfo> fields = fields(base.source(), env, sym, base.fields()); return new SourceTypeBoundClass( interfaceTypes.build(), @@ -69,7 +82,8 @@ public class CanonicalTypeBinder { base.memberImports(), base.annotationMetadata(), base.annotations(), - base.source()); + base.source(), + base.decl()); } private static ImmutableList<FieldInfo> fields( @@ -82,7 +96,7 @@ public class CanonicalTypeBinder { result.add( new FieldInfo( base.sym(), - Canonicalize.canonicalize(source, env, sym, base.type()), + Canonicalize.canonicalize(source, base.decl().position(), env, sym, base.type()), base.access(), base.annotations(), base.decl(), @@ -93,18 +107,21 @@ public class CanonicalTypeBinder { private static ImmutableList<MethodInfo> methods( SourceFile source, + int position, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ImmutableList<MethodInfo> methods) { ImmutableList.Builder<MethodInfo> result = ImmutableList.builder(); for (MethodInfo base : methods) { - ImmutableMap<TyVarSymbol, TyVarInfo> tps = typeParameters(source, env, sym, base.tyParams()); - Type ret = Canonicalize.canonicalize(source, env, sym, base.returnType()); + int pos = base.decl() != null ? base.decl().position() : position; + ImmutableMap<TyVarSymbol, TyVarInfo> tps = + typeParameters(source, pos, env, sym, base.tyParams()); + Type ret = Canonicalize.canonicalize(source, pos, env, sym, base.returnType()); ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder(); for (ParamInfo parameter : base.parameters()) { - parameters.add(param(source, env, sym, parameter)); + parameters.add(param(source, pos, env, sym, parameter)); } - ImmutableList<Type> exceptions = canonicalizeList(source, env, sym, base.exceptions()); + ImmutableList<Type> exceptions = canonicalizeList(source, pos, env, sym, base.exceptions()); result.add( new MethodInfo( base.sym(), @@ -116,15 +133,21 @@ public class CanonicalTypeBinder { base.defaultValue(), base.decl(), base.annotations(), - base.receiver() != null ? param(source, env, sym, base.receiver()) : null)); + base.receiver() != null + ? param(source, base.decl().position(), env, sym, base.receiver()) + : null)); } return result.build(); } private static ParamInfo param( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ParamInfo base) { + SourceFile source, + int position, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + ParamInfo base) { return new ParamInfo( - Canonicalize.canonicalize(source, env, sym, base.type()), + Canonicalize.canonicalize(source, position, env, sym, base.type()), base.name(), base.annotations(), base.access()); @@ -132,31 +155,28 @@ public class CanonicalTypeBinder { private static ImmutableMap<TyVarSymbol, TyVarInfo> typeParameters( SourceFile source, + int position, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, Map<TyVarSymbol, TyVarInfo> tps) { ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); for (Map.Entry<TyVarSymbol, TyVarInfo> e : tps.entrySet()) { TyVarInfo info = e.getValue(); - Type superClassBound = null; - if (info.superClassBound() != null) { - superClassBound = Canonicalize.canonicalize(source, env, sym, info.superClassBound()); - } - ImmutableList<Type> interfaceBounds = - canonicalizeList(source, env, sym, info.interfaceBounds()); - result.put(e.getKey(), new TyVarInfo(superClassBound, interfaceBounds, info.annotations())); + Type bound = Canonicalize.canonicalize(source, position, env, sym, info.bound()); + result.put(e.getKey(), new TyVarInfo((IntersectionTy) bound, info.annotations())); } return result.build(); } private static ImmutableList<Type> canonicalizeList( SourceFile source, + int position, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, ImmutableList<Type> types) { ImmutableList.Builder<Type> result = ImmutableList.builder(); for (Type type : types) { - result.add(Canonicalize.canonicalize(source, env, sym, type)); + result.add(Canonicalize.canonicalize(source, position, env, sym, type)); } return result.build(); } diff --git a/java/com/google/turbine/binder/ClassPathBinder.java b/java/com/google/turbine/binder/ClassPathBinder.java index 2b3a921..5d8db86 100644 --- a/java/com/google/turbine/binder/ClassPathBinder.java +++ b/java/com/google/turbine/binder/ClassPathBinder.java @@ -69,9 +69,7 @@ public class ClassPathBinder { } for (Map.Entry<ClassSymbol, BytecodeBoundClass> entry : transitive.entrySet()) { ClassSymbol symbol = entry.getKey(); - if (!map.containsKey(symbol)) { - map.put(symbol, entry.getValue()); - } + map.putIfAbsent(symbol, entry.getValue()); } SimpleEnv<ClassSymbol, BytecodeBoundClass> env = new SimpleEnv<>(ImmutableMap.copyOf(map)); SimpleEnv<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.copyOf(modules)); @@ -128,9 +126,8 @@ public class ClassPathBinder { continue; } ClassSymbol sym = new ClassSymbol(name.substring(0, name.length() - ".class".length())); - if (!env.containsKey(sym)) { - env.put(sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString())); - } + env.putIfAbsent( + sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, path.toString())); } } diff --git a/java/com/google/turbine/binder/CompUnitPreprocessor.java b/java/com/google/turbine/binder/CompUnitPreprocessor.java index 121b18d..85ff1c0 100644 --- a/java/com/google/turbine/binder/CompUnitPreprocessor.java +++ b/java/com/google/turbine/binder/CompUnitPreprocessor.java @@ -17,7 +17,6 @@ package com.google.turbine.binder; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -31,6 +30,7 @@ import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.CompUnit; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.ImportDecl; import com.google.turbine.tree.Tree.ModDecl; import com.google.turbine.tree.Tree.PkgDecl; @@ -38,6 +38,7 @@ import com.google.turbine.tree.Tree.TyDecl; import com.google.turbine.tree.TurbineModifier; import java.util.HashSet; import java.util.List; +import java.util.Optional; import java.util.Set; /** @@ -135,11 +136,11 @@ public class CompUnitPreprocessor { if (member.kind() == Tree.Kind.TY_DECL) { Tree.TyDecl decl = (Tree.TyDecl) member; ClassSymbol sym = new ClassSymbol(owner.binaryName() + '$' + decl.name()); - if (!seen.add(decl.name())) { + if (!seen.add(decl.name().value())) { throw TurbineError.format( source, member.position(), ErrorKind.DUPLICATE_DECLARATION, sym); } - result.put(decl.name(), sym); + result.put(decl.name().value(), sym); int access = innerClassAccess(enclosing, decl); @@ -213,9 +214,9 @@ public class CompUnitPreprocessor { pkgDecl.position(), ImmutableSet.of(TurbineModifier.ACC_SYNTHETIC), pkgDecl.annos(), - "package-info", + new Ident(pkgDecl.position(), "package-info"), ImmutableList.of(), - Optional.absent(), + Optional.empty(), ImmutableList.of(), ImmutableList.of(), TurbineTyKind.INTERFACE); diff --git a/java/com/google/turbine/binder/ConstBinder.java b/java/com/google/turbine/binder/ConstBinder.java index 4b48dd3..e5717b2 100644 --- a/java/com/google/turbine/binder/ConstBinder.java +++ b/java/com/google/turbine/binder/ConstBinder.java @@ -37,6 +37,7 @@ import com.google.turbine.model.Const; import com.google.turbine.model.Const.ArrayInitValue; import com.google.turbine.model.Const.Kind; import com.google.turbine.model.Const.Value; +import com.google.turbine.model.TurbineElementType; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.type.AnnoInfo; @@ -44,13 +45,13 @@ import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.IntersectionTy; import com.google.turbine.type.Type.TyKind; import com.google.turbine.type.Type.TyVar; import com.google.turbine.type.Type.WildLowerBoundedTy; import com.google.turbine.type.Type.WildTy; import com.google.turbine.type.Type.WildUnboundedTy; import com.google.turbine.type.Type.WildUpperBoundedTy; -import java.lang.annotation.ElementType; import java.lang.annotation.RetentionPolicy; import java.util.Map; @@ -91,8 +92,8 @@ public class ConstBinder { ImmutableList<TypeBoundClass.FieldInfo> fields = fields(base.fields()); ImmutableList<MethodInfo> methods = bindMethods(base.methods()); return new SourceTypeBoundClass( - bindClassTypes(base.interfaceTypes()), - base.superClassType() != null ? bindClassType(base.superClassType()) : null, + bindTypes(base.interfaceTypes()), + base.superClassType() != null ? bindType(base.superClassType()) : null, bindTypeParameters(base.typeParameterTypes()), base.access(), methods, @@ -106,7 +107,8 @@ public class ConstBinder { base.memberImports(), bindAnnotationMetadata(base.kind(), annos), annos, - base.source()); + base.source(), + base.decl()); } private ImmutableList<MethodInfo> bindMethods(ImmutableList<MethodInfo> methods) { @@ -156,7 +158,7 @@ public class ConstBinder { return null; } RetentionPolicy retention = null; - ImmutableSet<ElementType> target = null; + ImmutableSet<TurbineElementType> target = null; ClassSymbol repeatable = null; for (AnnoInfo annotation : annotations) { switch (annotation.sym().binaryName()) { @@ -188,8 +190,8 @@ public class ConstBinder { return RetentionPolicy.valueOf(enumValue.sym().name()); } - private static ImmutableSet<ElementType> bindTarget(AnnoInfo annotation) { - ImmutableSet.Builder<ElementType> result = ImmutableSet.builder(); + private static ImmutableSet<TurbineElementType> bindTarget(AnnoInfo annotation) { + ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder(); Const val = annotation.values().get("value"); switch (val.kind()) { case ARRAY: @@ -221,9 +223,9 @@ public class ConstBinder { } private static void bindTargetElement( - ImmutableSet.Builder<ElementType> target, EnumConstantValue enumVal) { + ImmutableSet.Builder<TurbineElementType> target, EnumConstantValue enumVal) { if (enumVal.sym().owner().binaryName().equals("java/lang/annotation/ElementType")) { - target.add(ElementType.valueOf(enumVal.sym().name())); + target.add(TurbineElementType.valueOf(enumVal.sym().name())); } } @@ -268,14 +270,6 @@ public class ConstBinder { return value; } - private ImmutableList<ClassTy> bindClassTypes(ImmutableList<ClassTy> types) { - ImmutableList.Builder<ClassTy> result = ImmutableList.builder(); - for (ClassTy t : types) { - result.add(bindClassType(t)); - } - return result.build(); - } - private ImmutableList<Type> bindTypes(ImmutableList<Type> types) { ImmutableList.Builder<Type> result = ImmutableList.builder(); for (Type t : types) { @@ -292,8 +286,7 @@ public class ConstBinder { result.put( entry.getKey(), new TyVarInfo( - info.superClassBound() != null ? bindType(info.superClassBound()) : null, - bindTypes(info.interfaceBounds()), + (IntersectionTy) bindType(info.bound()), constEvaluator.evaluateAnnotations(info.annotations()))); } return result.build(); @@ -303,25 +296,26 @@ public class ConstBinder { switch (type.tyKind()) { case TY_VAR: TyVar tyVar = (TyVar) type; - return new TyVar(tyVar.sym(), constEvaluator.evaluateAnnotations(tyVar.annos())); + return TyVar.create(tyVar.sym(), constEvaluator.evaluateAnnotations(tyVar.annos())); case CLASS_TY: return bindClassType((ClassTy) type); case ARRAY_TY: ArrayTy arrayTy = (ArrayTy) type; - return new ArrayTy( + return ArrayTy.create( bindType(arrayTy.elementType()), constEvaluator.evaluateAnnotations(arrayTy.annos())); case WILD_TY: { WildTy wildTy = (WildTy) type; switch (wildTy.boundKind()) { case NONE: - return new WildUnboundedTy(constEvaluator.evaluateAnnotations(wildTy.annotations())); + return WildUnboundedTy.create( + constEvaluator.evaluateAnnotations(wildTy.annotations())); case UPPER: - return new WildUpperBoundedTy( + return WildUpperBoundedTy.create( bindType(wildTy.bound()), constEvaluator.evaluateAnnotations(wildTy.annotations())); case LOWER: - return new WildLowerBoundedTy( + return WildLowerBoundedTy.create( bindType(wildTy.bound()), constEvaluator.evaluateAnnotations(wildTy.annotations())); default: @@ -331,6 +325,8 @@ public class ConstBinder { case PRIM_TY: case VOID_TY: return type; + case INTERSECTION_TY: + return IntersectionTy.create(bindTypes(((IntersectionTy) type).bounds())); default: throw new AssertionError(type.tyKind()); } @@ -339,11 +335,11 @@ public class ConstBinder { private ClassTy bindClassType(ClassTy type) { ClassTy classTy = type; ImmutableList.Builder<SimpleClassTy> classes = ImmutableList.builder(); - for (SimpleClassTy c : classTy.classes) { + for (SimpleClassTy c : classTy.classes()) { classes.add( - new SimpleClassTy( + SimpleClassTy.create( c.sym(), bindTypes(c.targs()), constEvaluator.evaluateAnnotations(c.annos()))); } - return new ClassTy(classes.build()); + return ClassTy.create(classes.build()); } } diff --git a/java/com/google/turbine/binder/ConstEvaluator.java b/java/com/google/turbine/binder/ConstEvaluator.java index 7e79919..2d8ce2d 100644 --- a/java/com/google/turbine/binder/ConstEvaluator.java +++ b/java/com/google/turbine/binder/ConstEvaluator.java @@ -39,6 +39,7 @@ import com.google.turbine.diag.SourceFile; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; +import com.google.turbine.model.Const.ConstCastError; import com.google.turbine.model.Const.Value; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; @@ -50,6 +51,7 @@ import com.google.turbine.tree.Tree.ClassTy; import com.google.turbine.tree.Tree.Conditional; import com.google.turbine.tree.Tree.ConstVarName; import com.google.turbine.tree.Tree.Expression; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.PrimTy; import com.google.turbine.tree.Tree.TypeCast; import com.google.turbine.tree.Tree.Unary; @@ -170,13 +172,13 @@ public strictfp class ConstEvaluator { private Type evalClassLiteralType(Tree.Type type) { switch (type.kind()) { case PRIM_TY: - return new Type.PrimTy(((PrimTy) type).tykind(), ImmutableList.of()); + return Type.PrimTy.create(((PrimTy) type).tykind(), ImmutableList.of()); case VOID_TY: return Type.VOID; case CLASS_TY: return Type.ClassTy.asNonParametricClassTy(resolveClass((ClassTy) type)); case ARR_TY: - return new Type.ArrayTy( + return Type.ArrayTy.create( evalClassLiteralType(((Tree.ArrTy) type).elem()), ImmutableList.of()); default: throw new AssertionError(type.kind()); @@ -193,22 +195,22 @@ public strictfp class ConstEvaluator { * isn't completed during the hierarchy phase). */ private ClassSymbol resolveClass(ClassTy classTy) { - ArrayDeque<String> flat = new ArrayDeque<>(); - for (ClassTy curr = classTy; curr != null; curr = curr.base().orNull()) { + ArrayDeque<Ident> flat = new ArrayDeque<>(); + for (ClassTy curr = classTy; curr != null; curr = curr.base().orElse(null)) { flat.addFirst(curr.name()); } - LookupResult result = scope.lookup(new LookupKey(flat)); + LookupResult result = scope.lookup(new LookupKey(ImmutableList.copyOf(flat))); if (result == null) { throw error(classTy.position(), ErrorKind.CANNOT_RESOLVE, flat.peekFirst()); } ClassSymbol classSym = (ClassSymbol) result.sym(); - for (String bit : result.remaining()) { + for (Ident bit : result.remaining()) { classSym = resolveNext(classTy.position(), classSym, bit); } return classSym; } - private ClassSymbol resolveNext(int position, ClassSymbol sym, String bit) { + private ClassSymbol resolveNext(int position, ClassSymbol sym, Ident bit) { ClassSymbol next = Resolve.resolve(env, origin, sym, bit); if (next == null) { throw error( @@ -233,7 +235,7 @@ public strictfp class ConstEvaluator { } FieldInfo resolveField(ConstVarName t) { - String simpleName = t.name().get(0); + Ident simpleName = t.name().get(0); FieldInfo field = lexicalField(env, owner, simpleName); if (field != null) { return field; @@ -242,7 +244,7 @@ public strictfp class ConstEvaluator { if (field != null) { return field; } - ClassSymbol classSymbol = memberImports.singleMemberImport(simpleName); + ClassSymbol classSymbol = memberImports.singleMemberImport(simpleName.value()); if (classSymbol != null) { field = Resolve.resolveField(env, origin, classSymbol, simpleName); if (field != null) { @@ -292,7 +294,7 @@ public strictfp class ConstEvaluator { /** Search for constant variables in lexically enclosing scopes. */ private FieldInfo lexicalField( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, String name) { + Env<ClassSymbol, TypeBoundClass> env, ClassSymbol sym, Ident name) { while (sym != null) { TypeBoundClass info = env.get(sym); FieldInfo field = Resolve.resolveField(env, origin, sym, name); @@ -442,8 +444,9 @@ public strictfp class ConstEvaluator { { ClassTy classTy = (ClassTy) t.ty(); // TODO(cushon): check package? - if (!classTy.name().equals("String")) { - throw new AssertionError(classTy); + if (!classTy.name().value().equals("String")) { + // Explicit boxing cases (e.g. `(Boolean) false`) are legal, but not const exprs. + return null; } return expr.asString(); } @@ -912,13 +915,13 @@ public strictfp class ConstEvaluator { template.put(method.name(), method.returnType()); } - ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); + Map<String, Const> values = new LinkedHashMap<>(); for (Expression arg : info.args()) { Expression expr; String key; if (arg.kind() == Tree.Kind.ASSIGN) { Tree.Assign assign = (Tree.Assign) arg; - key = assign.name(); + key = assign.name().value(); expr = assign.expr(); } else { // expand the implicit 'value' name; `@Foo(42)` is sugar for `@Foo(value=42)` @@ -927,21 +930,27 @@ public strictfp class ConstEvaluator { } Type ty = template.get(key); if (ty == null) { - throw error(arg.position(), ErrorKind.CANNOT_RESOLVE, key); + throw error( + arg.position(), + ErrorKind.CANNOT_RESOLVE, + String.format("element %s() in %s", key, info.sym())); } Const value = evalAnnotationValue(expr, ty); if (value == null) { throw error(expr.position(), ErrorKind.EXPRESSION_ERROR); } - values.put(key, value); + Const existing = values.put(key, value); + if (existing != null) { + throw error(arg.position(), ErrorKind.INVALID_ANNOTATION_ARGUMENT); + } } - return info.withValues(values.build()); + return info.withValues(ImmutableMap.copyOf(values)); } private AnnotationValue evalAnno(Tree.Anno t) { LookupResult result = scope.lookup(new LookupKey(t.name())); ClassSymbol sym = (ClassSymbol) result.sym(); - for (String name : result.remaining()) { + for (Ident name : result.remaining()) { sym = Resolve.resolve(env, sym, sym, name); } AnnoInfo annoInfo = evaluateAnnotation(new AnnoInfo(source, sym, t, null)); @@ -997,15 +1006,14 @@ public strictfp class ConstEvaluator { } public Const.Value evalFieldInitializer(Expression expression, Type type) { - Const value; try { - value = eval(expression); - } catch (TurbineError error) { - return null; - } - if (value == null || value.kind() != Const.Kind.PRIMITIVE) { + Const value = eval(expression); + if (value == null || value.kind() != Const.Kind.PRIMITIVE) { + return null; + } + return (Const.Value) cast(type, value); + } catch (TurbineError | ConstCastError error) { return null; } - return (Const.Value) cast(type, value); } } diff --git a/java/com/google/turbine/binder/CtSymClassBinder.java b/java/com/google/turbine/binder/CtSymClassBinder.java index 0d71b8d..fd2e544 100644 --- a/java/com/google/turbine/binder/CtSymClassBinder.java +++ b/java/com/google/turbine/binder/CtSymClassBinder.java @@ -20,6 +20,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.bound.ModuleInfo; +import com.google.turbine.binder.bytecode.BytecodeBinder; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.env.SimpleEnv; @@ -47,6 +48,7 @@ public class CtSymClassBinder { throw new IllegalStateException("lib/ct.sym does not exist in " + javaHome); } Map<ClassSymbol, BytecodeBoundClass> map = new HashMap<>(); + Map<ModuleSymbol, ModuleInfo> modules = new HashMap<>(); Env<ClassSymbol, BytecodeBoundClass> benv = new Env<ClassSymbol, BytecodeBoundClass>() { @Override @@ -70,19 +72,21 @@ public class CtSymClassBinder { if (!ze.name().substring(0, idx).contains(version)) { continue; } - ClassSymbol sym = new ClassSymbol(name.substring(idx + 1, name.length() - ".sig".length())); - if (!map.containsKey(sym)) { - map.put( - sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, ctSym + "!" + ze.name())); + if (name.substring(name.lastIndexOf('/') + 1).equals("module-info.sig")) { + ModuleInfo moduleInfo = BytecodeBinder.bindModuleInfo(name, toByteArrayOrDie(ze)); + modules.put(new ModuleSymbol(moduleInfo.name()), moduleInfo); + continue; } + ClassSymbol sym = new ClassSymbol(name.substring(idx + 1, name.length() - ".sig".length())); + map.putIfAbsent( + sym, new BytecodeBoundClass(sym, toByteArrayOrDie(ze), benv, ctSym + "!" + ze.name())); } if (map.isEmpty()) { // we didn't find any classes for the desired release return null; } SimpleEnv<ClassSymbol, BytecodeBoundClass> env = new SimpleEnv<>(ImmutableMap.copyOf(map)); - // TODO(cushon): support ct.sym module-infos once they exist (JDK 10?) - Env<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.of()); + Env<ModuleSymbol, ModuleInfo> moduleEnv = new SimpleEnv<>(ImmutableMap.copyOf(modules)); TopLevelIndex index = SimpleTopLevelIndex.of(env.asMap().keySet()); return new ClassPath() { @Override diff --git a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java index 26dce32..9ad20d2 100644 --- a/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java +++ b/java/com/google/turbine/binder/DisambiguateTypeAnnotations.java @@ -18,10 +18,10 @@ package com.google.turbine.binder; import static com.google.common.collect.Iterables.getOnlyElement; +import com.google.common.collect.ArrayListMultimap; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableList.Builder; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.LinkedHashMultimap; import com.google.common.collect.Multimap; import com.google.turbine.binder.bound.AnnotationValue; import com.google.turbine.binder.bound.SourceTypeBoundClass; @@ -34,6 +34,7 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; +import com.google.turbine.model.TurbineElementType; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; @@ -41,7 +42,6 @@ import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; import com.google.turbine.type.Type.PrimTy; import com.google.turbine.type.Type.TyVar; -import java.lang.annotation.ElementType; import java.util.Collection; import java.util.Map; import java.util.Set; @@ -84,7 +84,8 @@ public class DisambiguateTypeAnnotations { base.memberImports(), base.annotationMetadata(), groupRepeated(env, base.annotations()), - base.source()); + base.source(), + base.decl()); } private static ImmutableList<MethodInfo> bindMethods( @@ -101,7 +102,9 @@ public class DisambiguateTypeAnnotations { Type returnType = disambiguate( env, - base.name().equals("<init>") ? ElementType.CONSTRUCTOR : ElementType.METHOD, + base.name().equals("<init>") + ? TurbineElementType.CONSTRUCTOR + : TurbineElementType.METHOD, base.returnType(), base.annotations(), declarationAnnotations); @@ -131,7 +134,11 @@ public class DisambiguateTypeAnnotations { ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); Type type = disambiguate( - env, ElementType.PARAMETER, base.type(), base.annotations(), declarationAnnotations); + env, + TurbineElementType.PARAMETER, + base.type(), + base.annotations(), + declarationAnnotations); return new ParamInfo(type, base.name(), declarationAnnotations.build(), base.access()); } @@ -141,7 +148,7 @@ public class DisambiguateTypeAnnotations { */ private static Type disambiguate( Env<ClassSymbol, TypeBoundClass> env, - ElementType declarationTarget, + TurbineElementType declarationTarget, Type type, ImmutableList<AnnoInfo> annotations, Builder<AnnoInfo> declarationAnnotations) { @@ -150,8 +157,8 @@ public class DisambiguateTypeAnnotations { annotations = groupRepeated(env, annotations); ImmutableList.Builder<AnnoInfo> typeAnnotations = ImmutableList.builder(); for (AnnoInfo anno : annotations) { - Set<ElementType> target = env.get(anno.sym()).annotationMetadata().target(); - if (target.contains(ElementType.TYPE_USE)) { + Set<TurbineElementType> target = env.get(anno.sym()).annotationMetadata().target(); + if (target.contains(TurbineElementType.TYPE_USE)) { typeAnnotations.add(anno); } if (target.contains(declarationTarget)) { @@ -174,7 +181,7 @@ public class DisambiguateTypeAnnotations { ImmutableList.Builder<AnnoInfo> declarationAnnotations = ImmutableList.builder(); Type type = disambiguate( - env, ElementType.FIELD, base.type(), base.annotations(), declarationAnnotations); + env, TurbineElementType.FIELD, base.type(), base.annotations(), declarationAnnotations); return new FieldInfo( base.sym(), type, base.access(), declarationAnnotations.build(), base.decl(), base.value()); } @@ -193,23 +200,23 @@ public class DisambiguateTypeAnnotations { switch (type.tyKind()) { case PRIM_TY: PrimTy primTy = (PrimTy) type; - return new Type.PrimTy(primTy.primkind(), appendAnnotations(primTy.annos(), extra)); + return Type.PrimTy.create(primTy.primkind(), appendAnnotations(primTy.annos(), extra)); case CLASS_TY: ClassTy classTy = (ClassTy) type; - SimpleClassTy base = classTy.classes.get(0); + SimpleClassTy base = classTy.classes().get(0); SimpleClassTy simple = - new SimpleClassTy(base.sym(), base.targs(), appendAnnotations(base.annos(), extra)); - return new Type.ClassTy( + SimpleClassTy.create(base.sym(), base.targs(), appendAnnotations(base.annos(), extra)); + return Type.ClassTy.create( ImmutableList.<SimpleClassTy>builder() .add(simple) - .addAll(classTy.classes.subList(1, classTy.classes.size())) + .addAll(classTy.classes().subList(1, classTy.classes().size())) .build()); case ARRAY_TY: ArrayTy arrayTy = (ArrayTy) type; - return new ArrayTy(addAnnotationsToType(arrayTy.elementType(), extra), arrayTy.annos()); + return ArrayTy.create(addAnnotationsToType(arrayTy.elementType(), extra), arrayTy.annos()); case TY_VAR: TyVar tyVar = (TyVar) type; - return new Type.TyVar(tyVar.sym(), appendAnnotations(tyVar.annos(), extra)); + return Type.TyVar.create(tyVar.sym(), appendAnnotations(tyVar.annos(), extra)); case VOID_TY: return type; case WILD_TY: @@ -230,12 +237,12 @@ public class DisambiguateTypeAnnotations { * <p>For example, convert {@code @Foo @Foo} to {@code @Foos({@Foo, @Foo})}. * * <p>This method is used by {@link DisambiguateTypeAnnotations} for declaration annotations, and - * by {@link Lower} for type annotations. We could group type annotations here, but it would - * require another rewrite pass. + * by {@link com.google.turbine.lower.Lower} for type annotations. We could group type annotations + * here, but it would require another rewrite pass. */ public static ImmutableList<AnnoInfo> groupRepeated( Env<ClassSymbol, TypeBoundClass> env, ImmutableList<AnnoInfo> annotations) { - Multimap<ClassSymbol, AnnoInfo> repeated = LinkedHashMultimap.create(); + Multimap<ClassSymbol, AnnoInfo> repeated = ArrayListMultimap.create(); for (AnnoInfo anno : annotations) { repeated.put(anno.sym(), anno); } diff --git a/java/com/google/turbine/binder/HierarchyBinder.java b/java/com/google/turbine/binder/HierarchyBinder.java index 2545c17..8549d3a 100644 --- a/java/com/google/turbine/binder/HierarchyBinder.java +++ b/java/com/google/turbine/binder/HierarchyBinder.java @@ -27,8 +27,8 @@ import com.google.turbine.binder.lookup.LookupKey; import com.google.turbine.binder.lookup.LookupResult; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; -import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; +import com.google.turbine.diag.TurbineLog.TurbineLogWithSource; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ClassTy; @@ -39,20 +39,24 @@ public class HierarchyBinder { /** Binds the type hierarchy (superclasses and interfaces) for a single class. */ public static SourceHeaderBoundClass bind( + TurbineLogWithSource log, ClassSymbol origin, PackageSourceBoundClass base, Env<ClassSymbol, ? extends HeaderBoundClass> env) { - return new HierarchyBinder(origin, base, env).bind(); + return new HierarchyBinder(log, origin, base, env).bind(); } + private final TurbineLogWithSource log; private final ClassSymbol origin; private final PackageSourceBoundClass base; private final Env<ClassSymbol, ? extends HeaderBoundClass> env; private HierarchyBinder( + TurbineLogWithSource log, ClassSymbol origin, PackageSourceBoundClass base, Env<ClassSymbol, ? extends HeaderBoundClass> env) { + this.log = log; this.origin = origin; this.base = base; this.env = env; @@ -84,7 +88,7 @@ public class HierarchyBinder { for (Tree.ClassTy i : decl.impls()) { ClassSymbol result = resolveClass(i); if (result == null) { - throw new AssertionError(i); + continue; } interfaces.add(result); } @@ -96,13 +100,12 @@ public class HierarchyBinder { ImmutableMap.Builder<String, TyVarSymbol> typeParameters = ImmutableMap.builder(); for (Tree.TyParam p : decl.typarams()) { - typeParameters.put(p.name(), new TyVarSymbol(origin, p.name())); + typeParameters.put(p.name().value(), new TyVarSymbol(origin, p.name().value())); } return new SourceHeaderBoundClass(base, superclass, interfaces.build(), typeParameters.build()); } - /** * Resolves the {@link ClassSymbol} for the given {@link Tree.ClassTy}, with handling for * non-canonical qualified type names. @@ -110,34 +113,41 @@ public class HierarchyBinder { private ClassSymbol resolveClass(Tree.ClassTy ty) { // flatten a left-recursive qualified type name to its component simple names // e.g. Foo<Bar>.Baz -> ["Foo", "Bar"] - ArrayDeque<String> flat = new ArrayDeque<>(); - for (Tree.ClassTy curr = ty; curr != null; curr = curr.base().orNull()) { + ArrayDeque<Tree.Ident> flat = new ArrayDeque<>(); + for (Tree.ClassTy curr = ty; curr != null; curr = curr.base().orElse(null)) { flat.addFirst(curr.name()); } // Resolve the base symbol in the qualified name. - LookupResult result = lookup(ty, new LookupKey(flat)); + LookupResult result = lookup(ty, new LookupKey(ImmutableList.copyOf(flat))); if (result == null) { - throw TurbineError.format(base.source(), ty.position(), ErrorKind.CANNOT_RESOLVE, ty); + log.error(ty.position(), ErrorKind.CANNOT_RESOLVE, ty); + return null; } // Resolve pieces in the qualified name referring to member types. // This needs to consider member type declarations inherited from supertypes and interfaces. ClassSymbol sym = (ClassSymbol) result.sym(); - for (String bit : result.remaining()) { + for (Tree.Ident bit : result.remaining()) { sym = resolveNext(ty, sym, bit); + if (sym == null) { + break; + } } return sym; } - private ClassSymbol resolveNext(ClassTy ty, ClassSymbol sym, String bit) { + private ClassSymbol resolveNext(ClassTy ty, ClassSymbol sym, Tree.Ident bit) { ClassSymbol next; try { next = Resolve.resolve(env, origin, sym, bit); } catch (LazyBindingError e) { - throw error(ty.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); + log.error(ty.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); + return null; } if (next == null) { - throw error( - ty.position(), ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(sym.binaryName() + '$' + bit)); + log.error( + bit.position(), + ErrorKind.SYMBOL_NOT_FOUND, + new ClassSymbol(sym.binaryName() + '$' + bit)); } return next; } @@ -152,7 +162,8 @@ public class HierarchyBinder { try { result = Resolve.resolve(env, origin, curr, lookup.first()); } catch (LazyBindingError e) { - throw error(tree.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); + log.error(tree.position(), ErrorKind.CYCLIC_HIERARCHY, e.getMessage()); + result = null; } if (result != null) { return new LookupResult(result, lookup); @@ -162,8 +173,4 @@ public class HierarchyBinder { // qualified name resolution). return base.scope().lookup(lookup, Resolve.resolveFunction(env, origin)); } - - private TurbineError error(int position, ErrorKind kind, Object... args) { - return TurbineError.format(base.source(), position, kind, args); - } } diff --git a/java/com/google/turbine/binder/JimageClassBinder.java b/java/com/google/turbine/binder/JimageClassBinder.java index 40be3a3..72bcb2f 100644 --- a/java/com/google/turbine/binder/JimageClassBinder.java +++ b/java/com/google/turbine/binder/JimageClassBinder.java @@ -35,6 +35,7 @@ import com.google.turbine.binder.lookup.Scope; import com.google.turbine.binder.lookup.TopLevelIndex; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.ModuleSymbol; +import com.google.turbine.tree.Tree.Ident; import java.io.IOException; import java.io.UncheckedIOException; import java.net.URI; @@ -112,12 +113,8 @@ public class JimageClassBinder { if (path == null) { return null; } - try { - path = path.resolve("module-info.class"); - result = BytecodeBinder.bindModuleInfo(path.toString(), toByteArrayOrDie(path)); - } catch (IOException e) { - throw new UncheckedIOException(e); - } + path = path.resolve("module-info.class"); + result = BytecodeBinder.bindModuleInfo(path.toString(), toByteArrayOrDie(path)); moduleMap.put(moduleName, result); } return result; @@ -197,10 +194,15 @@ public class JimageClassBinder { // Find the longest prefix of the key that corresponds to a package name. // TODO(cushon): SimpleTopLevelIndex uses a prefix map for this, does it matter? Scope scope = null; - ImmutableList<String> names = lookupKey.simpleNames(); + ImmutableList<Ident> names = lookupKey.simpleNames(); + ImmutableList.Builder<String> flatNamesBuilder = ImmutableList.builder(); + for (Ident name : names) { + flatNamesBuilder.add(name.value()); + } + ImmutableList<String> flatNames = flatNamesBuilder.build(); int idx = -1; for (int i = 1; i < names.size(); i++) { - Scope cand = lookupPackage(names.subList(0, i)); + Scope cand = lookupPackage(flatNames.subList(0, i)); if (cand != null) { scope = cand; idx = i; @@ -227,7 +229,7 @@ public class JimageClassBinder { @Nullable @Override public LookupResult lookup(LookupKey lookupKey) { - ClassSymbol sym = packageClassesBySimpleName.get(packageName, lookupKey.first()); + ClassSymbol sym = packageClassesBySimpleName.get(packageName, lookupKey.first().value()); return sym != null ? new LookupResult(sym, lookupKey) : null; } }; diff --git a/java/com/google/turbine/binder/ModuleBinder.java b/java/com/google/turbine/binder/ModuleBinder.java index 312ec45..afd8770 100644 --- a/java/com/google/turbine/binder/ModuleBinder.java +++ b/java/com/google/turbine/binder/ModuleBinder.java @@ -19,7 +19,6 @@ package com.google.turbine.binder; import static com.google.common.base.Verify.verifyNotNull; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.bound.ModuleInfo; @@ -43,6 +42,7 @@ import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.TurbineFlag; import com.google.turbine.tree.Tree; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.ModDirective; import com.google.turbine.tree.Tree.ModExports; import com.google.turbine.tree.Tree.ModOpens; @@ -51,6 +51,7 @@ import com.google.turbine.tree.Tree.ModRequires; import com.google.turbine.tree.Tree.ModUses; import com.google.turbine.tree.TurbineModifier; import com.google.turbine.type.AnnoInfo; +import java.util.Optional; /** Binding pass for modules. */ public class ModuleBinder { @@ -149,7 +150,7 @@ public class ModuleBinder { return new SourceModuleInfo( module.module().moduleName(), - moduleVersion.orNull(), + moduleVersion.orElse(null), flags, annos, requires.build(), @@ -195,14 +196,14 @@ public class ModuleBinder { private ProvideInfo bindProvides(ModProvides directive) { ClassSymbol sym = resolve(directive.position(), directive.typeName()); ImmutableList.Builder<ClassSymbol> impls = ImmutableList.builder(); - for (ImmutableList<String> impl : directive.implNames()) { + for (ImmutableList<Ident> impl : directive.implNames()) { impls.add(resolve(directive.position(), impl)); } return new ProvideInfo(sym, impls.build()); } /* Resolves qualified class names. */ - private ClassSymbol resolve(int pos, ImmutableList<String> simpleNames) { + private ClassSymbol resolve(int pos, ImmutableList<Tree.Ident> simpleNames) { LookupKey key = new LookupKey(simpleNames); LookupResult result = scope.lookup(key); if (result == null) { @@ -210,7 +211,7 @@ public class ModuleBinder { ErrorKind.SYMBOL_NOT_FOUND, pos, new ClassSymbol(Joiner.on('/').join(simpleNames))); } ClassSymbol sym = (ClassSymbol) result.sym(); - for (String name : result.remaining()) { + for (Tree.Ident name : result.remaining()) { sym = Resolve.resolve(env, /* origin= */ null, sym, name); if (sym == null) { throw error( diff --git a/java/com/google/turbine/binder/Resolve.java b/java/com/google/turbine/binder/Resolve.java index d722373..0a61844 100644 --- a/java/com/google/turbine/binder/Resolve.java +++ b/java/com/google/turbine/binder/Resolve.java @@ -16,8 +16,6 @@ package com.google.turbine.binder; -import com.google.common.base.Joiner; -import com.google.common.collect.ImmutableList; import com.google.turbine.binder.bound.BoundClass; import com.google.turbine.binder.bound.HeaderBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; @@ -29,7 +27,10 @@ import com.google.turbine.binder.lookup.CanonicalSymbolResolver; import com.google.turbine.binder.lookup.ImportScope.ResolveFunction; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.model.TurbineVisibility; +import com.google.turbine.tree.Tree; +import java.util.HashSet; import java.util.Objects; +import java.util.Set; /** Qualified name resolution. */ public class Resolve { @@ -43,24 +44,37 @@ public class Resolve { Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin, ClassSymbol sym, - String simpleName) { + Tree.Ident simpleName) { + return resolve(env, origin, sym, simpleName, new HashSet<>()); + } + + private static ClassSymbol resolve( + Env<ClassSymbol, ? extends HeaderBoundClass> env, + ClassSymbol origin, + ClassSymbol sym, + Tree.Ident simpleName, + Set<ClassSymbol> seen) { ClassSymbol result; + if (!seen.add(sym)) { + // Optimize multiple-interface-inheritance, and don't get stuck in cycles. + return null; + } HeaderBoundClass bound = env.get(sym); if (bound == null) { return null; } - result = bound.children().get(simpleName); + result = bound.children().get(simpleName.value()); if (result != null) { return result; } if (bound.superclass() != null) { - result = resolve(env, origin, bound.superclass(), simpleName); + result = resolve(env, origin, bound.superclass(), simpleName, seen); if (result != null && visible(origin, result, env.get(result))) { return result; } } for (ClassSymbol i : bound.interfaces()) { - result = resolve(env, origin, i, simpleName); + result = resolve(env, origin, i, simpleName, seen); if (result != null && visible(origin, result, env.get(result))) { return result; } @@ -76,7 +90,7 @@ public class Resolve { Env<ClassSymbol, ? extends HeaderBoundClass> env, ClassSymbol origin) { return new ResolveFunction() { @Override - public ClassSymbol resolveOne(ClassSymbol base, String name) { + public ClassSymbol resolveOne(ClassSymbol base, Tree.Ident name) { try { return Resolve.resolve(env, origin, base, name); } catch (LazyBindingError e) { @@ -93,19 +107,18 @@ public class Resolve { private final String packagename; private final CompoundEnv<ClassSymbol, BoundClass> env; - public CanonicalResolver( - ImmutableList<String> packagename, CompoundEnv<ClassSymbol, BoundClass> env) { - this.packagename = Joiner.on('/').join(packagename); + public CanonicalResolver(String packagename, CompoundEnv<ClassSymbol, BoundClass> env) { + this.packagename = packagename; this.env = env; } @Override - public ClassSymbol resolveOne(ClassSymbol sym, String bit) { + public ClassSymbol resolveOne(ClassSymbol sym, Tree.Ident bit) { BoundClass ci = env.get(sym); if (ci == null) { return null; } - sym = ci.children().get(bit); + sym = ci.children().get(bit.value()); if (sym == null) { return null; } @@ -138,24 +151,37 @@ public class Resolve { * superclasses or interfaces. */ public static FieldInfo resolveField( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol origin, ClassSymbol sym, String name) { + Env<ClassSymbol, TypeBoundClass> env, ClassSymbol origin, ClassSymbol sym, Tree.Ident name) { + return resolveField(env, origin, sym, name, new HashSet<>()); + } + + private static FieldInfo resolveField( + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol origin, + ClassSymbol sym, + Tree.Ident name, + Set<ClassSymbol> seen) { + if (!seen.add(sym)) { + // Optimize multiple-interface-inheritance, and don't get stuck in cycles. + return null; + } TypeBoundClass info = env.get(sym); if (info == null) { return null; } for (FieldInfo f : info.fields()) { - if (f.name().equals(name)) { + if (f.name().equals(name.value())) { return f; } } if (info.superclass() != null) { - FieldInfo field = resolveField(env, origin, info.superclass(), name); + FieldInfo field = resolveField(env, origin, info.superclass(), name, seen); if (field != null && visible(origin, field)) { return field; } } for (ClassSymbol i : info.interfaces()) { - FieldInfo field = resolveField(env, origin, i, name); + FieldInfo field = resolveField(env, origin, i, name, seen); if (field != null && visible(origin, field)) { return field; } diff --git a/java/com/google/turbine/binder/TypeBinder.java b/java/com/google/turbine/binder/TypeBinder.java index e0546ac..8cf71e1 100644 --- a/java/com/google/turbine/binder/TypeBinder.java +++ b/java/com/google/turbine/binder/TypeBinder.java @@ -36,20 +36,23 @@ import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.MethodSymbol; import com.google.turbine.binder.sym.Symbol; import com.google.turbine.binder.sym.TyVarSymbol; -import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; +import com.google.turbine.diag.TurbineLog.TurbineLogWithSource; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; import com.google.turbine.model.TurbineVisibility; import com.google.turbine.tree.Tree; +import com.google.turbine.tree.Tree.Anno; import com.google.turbine.tree.Tree.ClassTy; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.Kind; import com.google.turbine.tree.Tree.MethDecl; import com.google.turbine.tree.Tree.PrimTy; import com.google.turbine.tree.TurbineModifier; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; +import com.google.turbine.type.Type.IntersectionTy; import java.util.ArrayDeque; import java.util.ArrayList; import java.util.HashSet; @@ -73,7 +76,7 @@ public class TypeBinder { @Override public LookupResult lookup(LookupKey lookup) { - if (name.equals(lookup.first())) { + if (name.equals(lookup.first().value())) { return new LookupResult(sym, lookup); } return null; @@ -90,9 +93,8 @@ public class TypeBinder { @Override public LookupResult lookup(LookupKey lookupKey) { - return tps.containsKey(lookupKey.first()) - ? new LookupResult(tps.get(lookupKey.first()), lookupKey) - : null; + Symbol sym = tps.get(lookupKey.first().value()); + return sym != null ? new LookupResult(sym, lookupKey) : null; } } @@ -118,7 +120,7 @@ public class TypeBinder { if (result != null) { return new LookupResult(result, lookup); } - result = info.typeParameters().get(lookup.first()); + result = info.typeParameters().get(lookup.first().value()); if (result != null) { return new LookupResult(result, lookup); } @@ -130,16 +132,24 @@ public class TypeBinder { /** Creates {@link SourceTypeBoundClass} nodes for a compilation. */ public static SourceTypeBoundClass bind( - Env<ClassSymbol, HeaderBoundClass> env, ClassSymbol sym, SourceHeaderBoundClass base) { - return new TypeBinder(env, sym, base).bind(); + TurbineLogWithSource log, + Env<ClassSymbol, HeaderBoundClass> env, + ClassSymbol sym, + SourceHeaderBoundClass base) { + return new TypeBinder(log, env, sym, base).bind(); } + private final TurbineLogWithSource log; private final Env<ClassSymbol, HeaderBoundClass> env; private final ClassSymbol owner; private final SourceHeaderBoundClass base; private TypeBinder( - Env<ClassSymbol, HeaderBoundClass> env, ClassSymbol owner, SourceHeaderBoundClass base) { + TurbineLogWithSource log, + Env<ClassSymbol, HeaderBoundClass> env, + ClassSymbol owner, + SourceHeaderBoundClass base) { + this.log = log; this.env = env; this.owner = owner; this.base = base; @@ -154,7 +164,7 @@ public class TypeBinder { CompoundScope enclosingScope = base.scope() .toScope(Resolve.resolveFunction(env, owner)) - .append(new SingletonScope(base.decl().name(), owner)) + .append(new SingletonScope(base.decl().name().value(), owner)) .append(new ClassMemberScope(base.owner(), env)); ImmutableList<AnnoInfo> annotations = bindAnnotations(enclosingScope, base.decl().annos()); @@ -166,14 +176,14 @@ public class TypeBinder { final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes = bindTyParams(base.decl().typarams(), bindingScope, base.typeParameters()); - ImmutableList.Builder<Type.ClassTy> interfaceTypes = ImmutableList.builder(); - Type.ClassTy superClassType; + ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder(); + Type superClassType; switch (base.kind()) { case ENUM: superClassType = - new Type.ClassTy( + Type.ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + Type.ClassTy.SimpleClassTy.create( ClassSymbol.ENUM, ImmutableList.of(Type.ClassTy.asNonParametricClassTy(owner)), ImmutableList.of()))); @@ -184,7 +194,7 @@ public class TypeBinder { break; case CLASS: if (base.decl().xtnds().isPresent()) { - superClassType = (Type.ClassTy) bindClassTy(bindingScope, base.decl().xtnds().get()); + superClassType = bindClassTy(bindingScope, base.decl().xtnds().get()); } else if (owner.equals(ClassSymbol.OBJECT)) { // java.lang.Object doesn't have a superclass superClassType = null; @@ -203,13 +213,13 @@ public class TypeBinder { } for (Tree.ClassTy i : base.decl().impls()) { - interfaceTypes.add((Type.ClassTy) bindClassTy(bindingScope, i)); + interfaceTypes.add(bindClassTy(bindingScope, i)); } CompoundScope scope = base.scope() .toScope(Resolve.resolveFunction(env, owner)) - .append(new SingletonScope(base.decl().name(), owner)) + .append(new SingletonScope(base.decl().name().value(), owner)) .append(new ClassMemberScope(owner, env)); List<MethodInfo> methods = @@ -236,7 +246,8 @@ public class TypeBinder { base.memberImports(), /* annotation metadata */ null, annotations, - base.source()); + base.source(), + base.decl()); } /** Collect synthetic and implicit methods, including default constructors and enum methods. */ @@ -315,7 +326,7 @@ public class TypeBinder { /*synthetic*/ TurbineFlag.ACC_SYNTHETIC), new ParamInfo( - new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()), + Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), "$enum$ordinal", ImmutableList.of(), /*synthetic*/ @@ -332,7 +343,7 @@ public class TypeBinder { new MethodInfo( new MethodSymbol(owner, "values"), ImmutableMap.of(), - new Type.ArrayTy(Type.ClassTy.asNonParametricClassTy(owner), ImmutableList.of()), + Type.ArrayTy.create(Type.ClassTy.asNonParametricClassTy(owner), ImmutableList.of()), ImmutableList.of(), ImmutableList.of(), access | TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC, @@ -362,7 +373,7 @@ public class TypeBinder { if (m.kind() != Kind.METH_DECL) { continue; } - if (((MethDecl) m).name().equals("<init>")) { + if (((MethDecl) m).name().value().equals("<init>")) { return true; } } @@ -374,33 +385,21 @@ public class TypeBinder { ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) { ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); for (Tree.TyParam tree : trees) { - TyVarSymbol sym = symbols.get(tree.name()); - Type classBound = null; - ImmutableList.Builder<Type> interfaceBounds = ImmutableList.builder(); - boolean first = true; - for (Tree bound : tree.bounds()) { - Type ty = bindTy(scope, bound); - if (first && !isInterface(ty)) { - classBound = ty; - } else { - interfaceBounds.add(ty); + TyVarSymbol sym = symbols.get(tree.name().value()); + ImmutableList.Builder<Type> bounds = ImmutableList.builder(); + if (tree.bounds().isEmpty()) { + bounds.add(Type.ClassTy.OBJECT); + } else { + for (Tree bound : tree.bounds()) { + bounds.add(bindTy(scope, bound)); } - first = false; } ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, tree.annos()); - result.put(sym, new TyVarInfo(classBound, interfaceBounds.build(), annotations)); + result.put(sym, new TyVarInfo(IntersectionTy.create(bounds.build()), annotations)); } return result.build(); } - private boolean isInterface(Type ty) { - if (ty.tyKind() != Type.TyKind.CLASS_TY) { - return false; - } - HeaderBoundClass hi = env.get(((Type.ClassTy) ty).sym()); - return hi.kind() == TurbineTyKind.INTERFACE; - } - private List<MethodInfo> bindMethods(CompoundScope scope, ImmutableList<Tree> members) { List<MethodInfo> methods = new ArrayList<>(); for (Tree member : members) { @@ -413,13 +412,13 @@ public class TypeBinder { private MethodInfo bindMethod(CompoundScope scope, Tree.MethDecl t) { - MethodSymbol sym = new MethodSymbol(owner, t.name()); + MethodSymbol sym = new MethodSymbol(owner, t.name().value()); ImmutableMap<String, TyVarSymbol> typeParameters; { ImmutableMap.Builder<String, TyVarSymbol> builder = ImmutableMap.builder(); for (Tree.TyParam pt : t.typarams()) { - builder.put(pt.name(), new TyVarSymbol(owner, pt.name())); + builder.put(pt.name().value(), new TyVarSymbol(owner, pt.name().value())); } typeParameters = builder.build(); } @@ -437,7 +436,7 @@ public class TypeBinder { } ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder(); - String name = t.name(); + String name = t.name().value(); if (name.equals("<init>")) { if (hasEnclosingInstance(base)) { parameters.add(enclosingInstanceParameter()); @@ -454,10 +453,10 @@ public class TypeBinder { ParamInfo param = new ParamInfo( bindTy(scope, p.ty()), - p.name(), + p.name().value(), bindAnnotations(scope, p.annos()), /*synthetic*/ access); - if (p.name().equals("this")) { + if (p.name().value().equals("this")) { receiver = param; continue; } @@ -527,7 +526,8 @@ public class TypeBinder { if (member.kind() == Tree.Kind.VAR_DECL) { FieldInfo field = bindField(scope, (Tree.VarDecl) member); if (!seen.add(field.sym())) { - throw error(member.position(), ErrorKind.DUPLICATE_DECLARATION, "field: " + field.name()); + log.error(member.position(), ErrorKind.DUPLICATE_DECLARATION, "field: " + field.name()); + continue; } fields.add(field); } @@ -536,7 +536,7 @@ public class TypeBinder { } private FieldInfo bindField(CompoundScope scope, Tree.VarDecl decl) { - FieldSymbol sym = new FieldSymbol(owner, decl.name()); + FieldSymbol sym = new FieldSymbol(owner, decl.name().value()); Type type = bindTy(scope, decl.ty()); ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, decl.annos()); int access = 0; @@ -558,27 +558,40 @@ public class TypeBinder { CompoundScope scope, ImmutableList<Tree.Anno> trees) { ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); for (Tree.Anno tree : trees) { - LookupResult lookupResult = scope.lookup(new LookupKey(tree.name())); - if (lookupResult == null) { - throw error(tree.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(tree.name())); - } - ClassSymbol sym = (ClassSymbol) lookupResult.sym(); - for (String name : lookupResult.remaining()) { - sym = resolveNext(tree.position(), sym, name); - } - if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) { - throw error(tree.position(), ErrorKind.NOT_AN_ANNOTATION, sym); - } + ImmutableList<Ident> name = tree.name(); + LookupResult lookupResult = scope.lookup(new LookupKey(name)); + ClassSymbol sym = resolveAnnoSymbol(tree, name, lookupResult); result.add(new AnnoInfo(base.source(), sym, tree, null)); } return result.build(); } - private ClassSymbol resolveNext(int position, ClassSymbol sym, String bit) { + private ClassSymbol resolveAnnoSymbol( + Anno tree, ImmutableList<Ident> name, LookupResult lookupResult) { + if (lookupResult == null) { + log.error(tree.position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(name)); + return null; + } + ClassSymbol sym = (ClassSymbol) lookupResult.sym(); + for (Ident ident : lookupResult.remaining()) { + sym = resolveNext(sym, ident); + if (sym == null) { + return null; + } + } + if (env.get(sym).kind() != TurbineTyKind.ANNOTATION) { + log.error(tree.position(), ErrorKind.NOT_AN_ANNOTATION, sym); + } + return sym; + } + + private ClassSymbol resolveNext(ClassSymbol sym, Ident bit) { ClassSymbol next = Resolve.resolve(env, owner, sym, bit); if (next == null) { - throw error( - position, ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(sym.binaryName() + '$' + bit)); + log.error( + bit.position(), + ErrorKind.SYMBOL_NOT_FOUND, + new ClassSymbol(sym.binaryName() + '$' + bit)); } return next; } @@ -620,20 +633,22 @@ public class TypeBinder { ArrayList<Tree.ClassTy> flat; { ArrayDeque<Tree.ClassTy> builder = new ArrayDeque<>(); - for (Tree.ClassTy curr = t; curr != null; curr = curr.base().orNull()) { + for (Tree.ClassTy curr = t; curr != null; curr = curr.base().orElse(null)) { builder.addFirst(curr); } flat = new ArrayList<>(builder); } // the simple names of all classes in the qualified name - ArrayList<String> names = new ArrayList<>(); + ImmutableList.Builder<Tree.Ident> nameBuilder = ImmutableList.builder(); for (Tree.ClassTy curr : flat) { - names.add(curr.name()); + nameBuilder.add(curr.name()); } + ImmutableList<Tree.Ident> names = nameBuilder.build(); // resolve the prefix to a symbol LookupResult result = scope.lookup(new LookupKey(names)); if (result == null || result.sym() == null) { - throw error(t.position(), ErrorKind.CANNOT_RESOLVE, t); + log.error(names.get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(names)); + return Type.ErrorTy.create(); } Symbol sym = result.sym(); int annoIdx = flat.size() - result.remaining().size() - 1; @@ -644,9 +659,10 @@ public class TypeBinder { return bindClassTyRest(scope, flat, names, result, (ClassSymbol) sym, annos); case TY_PARAM: if (!result.remaining().isEmpty()) { - throw error(t.position(), ErrorKind.TYPE_PARAMETER_QUALIFIER); + log.error(t.position(), ErrorKind.TYPE_PARAMETER_QUALIFIER); + return Type.ErrorTy.create(); } - return new Type.TyVar((TyVarSymbol) sym, annos); + return Type.TyVar.create((TyVarSymbol) sym, annos); default: throw new AssertionError(sym.symKind()); } @@ -655,46 +671,45 @@ public class TypeBinder { private Type bindClassTyRest( CompoundScope scope, ArrayList<ClassTy> flat, - ArrayList<String> bits, + ImmutableList<Tree.Ident> bits, LookupResult result, ClassSymbol sym, ImmutableList<AnnoInfo> annotations) { int idx = bits.size() - result.remaining().size() - 1; ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder(); classes.add( - new Type.ClassTy.SimpleClassTy( + Type.ClassTy.SimpleClassTy.create( sym, bindTyArgs(scope, flat.get(idx++).tyargs()), annotations)); for (; idx < flat.size(); idx++) { Tree.ClassTy curr = flat.get(idx); - sym = resolveNext(curr.position(), sym, curr.name()); + sym = resolveNext(sym, curr.name()); + if (sym == null) { + return Type.ErrorTy.create(); + } annotations = bindAnnotations(scope, curr.annos()); classes.add( - new Type.ClassTy.SimpleClassTy(sym, bindTyArgs(scope, curr.tyargs()), annotations)); + Type.ClassTy.SimpleClassTy.create(sym, bindTyArgs(scope, curr.tyargs()), annotations)); } - return new Type.ClassTy(classes.build()); + return Type.ClassTy.create(classes.build()); } private Type.PrimTy bindPrimTy(CompoundScope scope, PrimTy t) { - return new Type.PrimTy(t.tykind(), bindAnnotations(scope, t.annos())); + return Type.PrimTy.create(t.tykind(), bindAnnotations(scope, t.annos())); } private Type bindArrTy(CompoundScope scope, Tree.ArrTy t) { - return new Type.ArrayTy(bindTy(scope, t.elem()), bindAnnotations(scope, t.annos())); + return Type.ArrayTy.create(bindTy(scope, t.elem()), bindAnnotations(scope, t.annos())); } private Type bindWildTy(CompoundScope scope, Tree.WildTy t) { ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, t.annos()); if (t.lower().isPresent()) { - return new Type.WildLowerBoundedTy(bindTy(scope, t.lower().get()), annotations); + return Type.WildLowerBoundedTy.create(bindTy(scope, t.lower().get()), annotations); } else if (t.upper().isPresent()) { - return new Type.WildUpperBoundedTy(bindTy(scope, t.upper().get()), annotations); + return Type.WildUpperBoundedTy.create(bindTy(scope, t.upper().get()), annotations); } else { - return new Type.WildUnboundedTy(annotations); + return Type.WildUnboundedTy.create(annotations); } } - - private TurbineError error(int position, ErrorKind kind, Object... args) { - return TurbineError.format(base.source(), position, kind, args); - } } diff --git a/java/com/google/turbine/binder/bound/AnnotationMetadata.java b/java/com/google/turbine/binder/bound/AnnotationMetadata.java index 1155e51..9c81a5f 100644 --- a/java/com/google/turbine/binder/bound/AnnotationMetadata.java +++ b/java/com/google/turbine/binder/bound/AnnotationMetadata.java @@ -20,7 +20,7 @@ import static com.google.common.base.MoreObjects.firstNonNull; import com.google.common.collect.ImmutableSet; import com.google.turbine.binder.sym.ClassSymbol; -import java.lang.annotation.ElementType; +import com.google.turbine.model.TurbineElementType; import java.lang.annotation.RetentionPolicy; import java.util.EnumSet; @@ -30,22 +30,22 @@ import java.util.EnumSet; */ public class AnnotationMetadata { - private static final ImmutableSet<ElementType> DEFAULT_TARGETS = getDefaultElements(); + private static final ImmutableSet<TurbineElementType> DEFAULT_TARGETS = getDefaultElements(); - private static ImmutableSet<ElementType> getDefaultElements() { - EnumSet<ElementType> values = EnumSet.allOf(ElementType.class); - values.remove(ElementType.TYPE_PARAMETER); - values.remove(ElementType.TYPE_USE); + private static ImmutableSet<TurbineElementType> getDefaultElements() { + EnumSet<TurbineElementType> values = EnumSet.allOf(TurbineElementType.class); + values.remove(TurbineElementType.TYPE_PARAMETER); + values.remove(TurbineElementType.TYPE_USE); return ImmutableSet.copyOf(values); } private final RetentionPolicy retention; - private final ImmutableSet<ElementType> target; + private final ImmutableSet<TurbineElementType> target; private final ClassSymbol repeatable; public AnnotationMetadata( RetentionPolicy retention, - ImmutableSet<ElementType> annotationTarget, + ImmutableSet<TurbineElementType> annotationTarget, ClassSymbol repeatable) { this.retention = firstNonNull(retention, RetentionPolicy.CLASS); this.target = firstNonNull(annotationTarget, DEFAULT_TARGETS); @@ -58,7 +58,7 @@ public class AnnotationMetadata { } /** Target element types specified by the {@code @Target} meta-annotation. */ - public ImmutableSet<ElementType> target() { + public ImmutableSet<TurbineElementType> target() { return target; } diff --git a/java/com/google/turbine/binder/bound/AnnotationValue.java b/java/com/google/turbine/binder/bound/AnnotationValue.java index 729a20f..fd4ffab 100644 --- a/java/com/google/turbine/binder/bound/AnnotationValue.java +++ b/java/com/google/turbine/binder/bound/AnnotationValue.java @@ -19,6 +19,7 @@ package com.google.turbine.binder.bound; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.model.Const; +import java.util.Objects; /** An annotation literal constant. */ public class AnnotationValue extends Const { @@ -50,4 +51,18 @@ public class AnnotationValue extends Const { public ImmutableMap<String, Const> values() { return values; } + + @Override + public int hashCode() { + return Objects.hash(sym, values); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AnnotationValue)) { + return false; + } + AnnotationValue that = (AnnotationValue) obj; + return sym().equals(that.sym()) && values().equals(that.values()); + } } diff --git a/java/com/google/turbine/binder/bound/ClassValue.java b/java/com/google/turbine/binder/bound/ClassValue.java index e06fc8d..a75db42 100644 --- a/java/com/google/turbine/binder/bound/ClassValue.java +++ b/java/com/google/turbine/binder/bound/ClassValue.java @@ -18,6 +18,7 @@ package com.google.turbine.binder.bound; import com.google.turbine.model.Const; import com.google.turbine.type.Type; +import java.util.Objects; /** A class literal constant. */ public class ClassValue extends Const { @@ -42,4 +43,14 @@ public class ClassValue extends Const { public Type type() { return type; } + + @Override + public int hashCode() { + return Objects.hash(type); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ClassValue && type().equals(((ClassValue) obj).type()); + } } diff --git a/java/com/google/turbine/binder/bound/EnumConstantValue.java b/java/com/google/turbine/binder/bound/EnumConstantValue.java index 07bb29c..4083ae3 100644 --- a/java/com/google/turbine/binder/bound/EnumConstantValue.java +++ b/java/com/google/turbine/binder/bound/EnumConstantValue.java @@ -36,4 +36,14 @@ public class EnumConstantValue extends Const { public Kind kind() { return Kind.ENUM_CONSTANT; } + + @Override + public int hashCode() { + return sym.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof EnumConstantValue && sym().equals(((EnumConstantValue) obj).sym()); + } } diff --git a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java index 73f2832..7314f72 100644 --- a/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/SourceTypeBoundClass.java @@ -16,19 +16,19 @@ package com.google.turbine.binder.bound; -import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; -import com.google.common.collect.Iterables; import com.google.turbine.binder.lookup.CompoundScope; import com.google.turbine.binder.lookup.MemberImportIndex; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.diag.SourceFile; import com.google.turbine.model.TurbineTyKind; +import com.google.turbine.tree.Tree; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.TyKind; import javax.annotation.Nullable; /** A HeaderBoundClass for classes compiled from source. */ @@ -42,8 +42,8 @@ public class SourceTypeBoundClass implements TypeBoundClass { private final ImmutableMap<String, TyVarSymbol> typeParameters; private final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes; - private final Type.ClassTy superClassType; - private final ImmutableList<Type.ClassTy> interfaceTypes; + private final Type superClassType; + private final ImmutableList<Type> interfaceTypes; private final ImmutableList<MethodInfo> methods; private final ImmutableList<FieldInfo> fields; private final CompoundScope enclosingScope; @@ -51,11 +51,12 @@ public class SourceTypeBoundClass implements TypeBoundClass { private final MemberImportIndex memberImports; private final AnnotationMetadata annotationMetadata; private final ImmutableList<AnnoInfo> annotations; + private final Tree.TyDecl decl; private final SourceFile source; public SourceTypeBoundClass( - ImmutableList<ClassTy> interfaceTypes, - ClassTy superClassType, + ImmutableList<Type> interfaceTypes, + Type superClassType, ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes, int access, ImmutableList<MethodInfo> methods, @@ -69,7 +70,8 @@ public class SourceTypeBoundClass implements TypeBoundClass { MemberImportIndex memberImports, AnnotationMetadata annotationMetadata, ImmutableList<AnnoInfo> annotations, - SourceFile source) { + SourceFile source, + Tree.TyDecl decl) { this.interfaceTypes = interfaceTypes; this.superClassType = superClassType; this.typeParameterTypes = typeParameterTypes; @@ -86,24 +88,29 @@ public class SourceTypeBoundClass implements TypeBoundClass { this.annotationMetadata = annotationMetadata; this.annotations = annotations; this.source = source; + this.decl = decl; } @Override public ClassSymbol superclass() { - return superClassType() != null ? superClassType().sym() : null; + if (superClassType == null) { + return null; + } + if (superClassType.tyKind() != TyKind.CLASS_TY) { + return null; + } + return ((ClassTy) superClassType).sym(); } @Override public ImmutableList<ClassSymbol> interfaces() { - return ImmutableList.copyOf( - Iterables.transform( - interfaceTypes, - new Function<ClassTy, ClassSymbol>() { - @Override - public ClassSymbol apply(ClassTy classTy) { - return classTy.sym(); - } - })); + ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder(); + for (Type type : interfaceTypes) { + if (type.tyKind() == TyKind.CLASS_TY) { + result.add(((ClassTy) type).sym()); + } + } + return result.build(); } @Override @@ -132,14 +139,14 @@ public class SourceTypeBoundClass implements TypeBoundClass { return typeParameters; } - /** Implemented interface types. */ - public ImmutableList<Type.ClassTy> interfaceTypes() { + @Override + public ImmutableList<Type> interfaceTypes() { return interfaceTypes; } /** The super-class type. */ @Override - public Type.ClassTy superClassType() { + public Type superClassType() { return superClassType; } @@ -181,7 +188,7 @@ public class SourceTypeBoundClass implements TypeBoundClass { return memberImports; } - /** Declaration annotations. */ + @Override public ImmutableList<AnnoInfo> annotations() { return annotations; } @@ -190,4 +197,8 @@ public class SourceTypeBoundClass implements TypeBoundClass { public SourceFile source() { return source; } + + public Tree.TyDecl decl() { + return decl; + } } diff --git a/java/com/google/turbine/binder/bound/TypeBoundClass.java b/java/com/google/turbine/binder/bound/TypeBoundClass.java index 560f0e1..350d311 100644 --- a/java/com/google/turbine/binder/bound/TypeBoundClass.java +++ b/java/com/google/turbine/binder/bound/TypeBoundClass.java @@ -27,12 +27,16 @@ import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.MethDecl; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; +import com.google.turbine.type.Type.IntersectionTy; /** A bound node that augments {@link HeaderBoundClass} with type information. */ public interface TypeBoundClass extends HeaderBoundClass { /** The super-class type. */ - Type.ClassTy superClassType(); + Type superClassType(); + + /** Implemented interface types. */ + ImmutableList<Type> interfaceTypes(); ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes(); @@ -48,29 +52,22 @@ public interface TypeBoundClass extends HeaderBoundClass { */ AnnotationMetadata annotationMetadata(); + /** Declaration annotations. */ + ImmutableList<AnnoInfo> annotations(); + /** A type parameter declaration. */ class TyVarInfo { - private final Type superClassBound; - private final ImmutableList<Type> interfaceBounds; + private final IntersectionTy bound; private final ImmutableList<AnnoInfo> annotations; - public TyVarInfo( - Type superClassBound, - ImmutableList<Type> interfaceBounds, - ImmutableList<AnnoInfo> annotations) { - this.superClassBound = superClassBound; - this.interfaceBounds = interfaceBounds; + public TyVarInfo(IntersectionTy bound, ImmutableList<AnnoInfo> annotations) { + this.bound = bound; this.annotations = annotations; } - /** A class bound, or {@code null}. */ - public Type superClassBound() { - return superClassBound; - } - - /** Interface type bounds. */ - public ImmutableList<Type> interfaceBounds() { - return interfaceBounds; + /** The bound. */ + public IntersectionTy bound() { + return bound; } /** Type parameter declaration annotations. */ diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java index 4b92f11..e5ba343 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBinder.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBinder.java @@ -17,21 +17,34 @@ package com.google.turbine.binder.bytecode; import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.turbine.binder.bound.AnnotationValue; +import com.google.turbine.binder.bound.ClassValue; +import com.google.turbine.binder.bound.EnumConstantValue; import com.google.turbine.binder.bound.ModuleInfo; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.bytecode.ClassFile; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; import com.google.turbine.bytecode.ClassReader; import com.google.turbine.bytecode.sig.Sig; import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig; import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig; import com.google.turbine.bytecode.sig.Sig.WildTySig; +import com.google.turbine.bytecode.sig.SigParser; +import com.google.turbine.model.Const; +import com.google.turbine.model.Const.ArrayInitValue; +import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; -import com.google.turbine.type.Type.ArrayTy; -import com.google.turbine.type.Type.TyVar; -import java.io.IOException; import java.util.ArrayList; import java.util.List; +import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -52,21 +65,21 @@ public class BytecodeBinder { tyArgs.add(bindTy(arg, scope)); } - classes.add(new Type.ClassTy.SimpleClassTy(sym, tyArgs.build(), ImmutableList.of())); + classes.add(Type.ClassTy.SimpleClassTy.create(sym, tyArgs.build(), ImmutableList.of())); first = false; } - return new Type.ClassTy(classes); + return Type.ClassTy.create(classes); } private static Type wildTy(WildTySig sig, Function<String, TyVarSymbol> scope) { switch (sig.boundKind()) { case NONE: - return new Type.WildUnboundedTy(ImmutableList.of()); + return Type.WildUnboundedTy.create(ImmutableList.of()); case LOWER: - return new Type.WildLowerBoundedTy( + return Type.WildLowerBoundedTy.create( bindTy(((LowerBoundTySig) sig).bound(), scope), ImmutableList.of()); case UPPER: - return new Type.WildUpperBoundedTy( + return Type.WildUpperBoundedTy.create( bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of()); default: throw new AssertionError(sig.boundKind()); @@ -76,22 +89,110 @@ public class BytecodeBinder { static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) { switch (sig.kind()) { case BASE_TY_SIG: - return new Type.PrimTy(((Sig.BaseTySig) sig).type(), ImmutableList.of()); + return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of()); case CLASS_TY_SIG: return bindClassTy((Sig.ClassTySig) sig, scope); case TY_VAR_SIG: - return new TyVar(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of()); + return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of()); case ARRAY_TY_SIG: return bindArrayTy((Sig.ArrayTySig) sig, scope); case WILD_TY_SIG: return wildTy((WildTySig) sig, scope); + case VOID_TY_SIG: + return Type.VOID; default: throw new AssertionError(sig.kind()); } } private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) { - return new ArrayTy(bindTy(arrayTySig.elementType(), scope), ImmutableList.of()); + return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of()); + } + + public static Const bindValue(Type type, ElementValue value) { + switch (value.kind()) { + case ENUM: + return bindEnumValue((EnumConstValue) value); + case CONST: + return bindConstValue(type, ((ConstValue) value).value()); + case ARRAY: + return bindArrayValue(type, (ArrayValue) value); + case CLASS: + return new ClassValue( + bindTy( + new SigParser(((ConstClassValue) value).className()).parseType(), + x -> { + throw new IllegalStateException(x); + })); + case ANNOTATION: + return bindAnnotationValue(type, ((ElementValue.AnnotationValue) value).annotation()); + } + throw new AssertionError(value.kind()); + } + + static AnnotationValue bindAnnotationValue(Type type, AnnotationInfo value) { + ClassSymbol sym = asClassSymbol(value.typeName()); + ImmutableMap.Builder<String, Const> values = ImmutableMap.builder(); + for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) { + values.put(e.getKey(), bindValue(type, e.getValue())); + } + return new AnnotationValue(sym, values.build()); + } + + static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) { + ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); + for (AnnotationInfo annotation : input) { + AnnotationValue anno = bindAnnotationValue(Type.VOID, annotation); + result.add(new AnnoInfo(null, anno.sym(), null, anno.values())); + } + return result.build(); + } + + private static ClassSymbol asClassSymbol(String s) { + return new ClassSymbol(s.substring(1, s.length() - 1)); + } + + private static Const bindArrayValue(Type type, ArrayValue value) { + ImmutableList.Builder<Const> elements = ImmutableList.builder(); + for (ElementValue element : value.elements()) { + elements.add(bindValue(type, element)); + } + return new ArrayInitValue(elements.build()); + } + + public static Const.Value bindConstValue(Type type, Const.Value value) { + if (type.tyKind() != Type.TyKind.PRIM_TY) { + return value; + } + // TODO(b/32626659): this is not bug-compatible with javac + switch (((Type.PrimTy) type).primkind()) { + case CHAR: + return new Const.CharValue(value.asChar().value()); + case SHORT: + return new Const.ShortValue(value.asShort().value()); + case INT: + return new Const.IntValue(value.asInteger().value()); + case LONG: + return new Const.LongValue(value.asLong().value()); + case FLOAT: + return new Const.FloatValue(value.asFloat().value()); + case DOUBLE: + return new Const.DoubleValue(value.asDouble().value()); + case BOOLEAN: + // boolean constants are encoded as integers + return new Const.BooleanValue(value.asInteger().value() != 0); + case BYTE: + return new Const.ByteValue(value.asByte().value()); + case STRING: + case NULL: + return value; + } + throw new AssertionError(type); + } + + private static Const bindEnumValue(EnumConstValue value) { + return new EnumConstantValue( + new FieldSymbol(asClassSymbol(value.typeName()), value.constName())); } /** @@ -99,7 +200,7 @@ public class BytecodeBinder { * version, and flags are populated, since the directives are not needed by turbine at compile * time. */ - public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) throws IOException { + public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) { ClassFile classFile = ClassReader.read(path, bytes.get()); ClassFile.ModuleInfo module = classFile.module(); return new ModuleInfo( diff --git a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java index 36c191f..f21760b 100644 --- a/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java +++ b/java/com/google/turbine/binder/bytecode/BytecodeBoundClass.java @@ -40,16 +40,21 @@ import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayVa import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue; import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.Kind; +import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo; import com.google.turbine.bytecode.ClassReader; import com.google.turbine.bytecode.sig.Sig; import com.google.turbine.bytecode.sig.Sig.ClassSig; +import com.google.turbine.bytecode.sig.Sig.ClassTySig; +import com.google.turbine.bytecode.sig.Sig.TySig; import com.google.turbine.bytecode.sig.SigParser; import com.google.turbine.model.Const; +import com.google.turbine.model.TurbineElementType; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; +import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ClassTy; -import java.lang.annotation.ElementType; +import com.google.turbine.type.Type.IntersectionTy; import java.lang.annotation.RetentionPolicy; import java.util.Map; import java.util.function.Function; @@ -72,7 +77,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou public BytecodeBoundClass( ClassSymbol sym, - final Supplier<byte[]> bytes, + Supplier<byte[]> bytes, Env<ClassSymbol, BytecodeBoundClass> env, String jarFile) { this.sym = sym; @@ -94,7 +99,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou }); } - final Supplier<TurbineTyKind> kind = + private final Supplier<TurbineTyKind> kind = Suppliers.memoize( new Supplier<TurbineTyKind>() { @Override @@ -118,7 +123,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return kind.get(); } - final Supplier<ClassSymbol> owner = + private final Supplier<ClassSymbol> owner = Suppliers.memoize( new Supplier<ClassSymbol>() { @Override @@ -138,7 +143,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return owner.get(); } - final Supplier<ImmutableMap<String, ClassSymbol>> children = + private final Supplier<ImmutableMap<String, ClassSymbol>> children = Suppliers.memoize( new Supplier<ImmutableMap<String, ClassSymbol>>() { @Override @@ -162,7 +167,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return children.get(); } - final Supplier<Integer> access = + private final Supplier<Integer> access = Suppliers.memoize( new Supplier<Integer>() { @Override @@ -217,7 +222,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return tyParams.get(); } - final Supplier<ClassSymbol> superclass = + private final Supplier<ClassSymbol> superclass = Suppliers.memoize( new Supplier<ClassSymbol>() { @Override @@ -235,7 +240,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return superclass.get(); } - final Supplier<ImmutableList<ClassSymbol>> interfaces = + private final Supplier<ImmutableList<ClassSymbol>> interfaces = Suppliers.memoize( new Supplier<ImmutableList<ClassSymbol>>() { @Override @@ -253,7 +258,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return interfaces.get(); } - Supplier<ClassTy> superClassType = + private final Supplier<ClassTy> superClassType = Suppliers.memoize( new Supplier<ClassTy>() { @Override @@ -274,7 +279,35 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return superClassType.get(); } - Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes = + private final Supplier<ImmutableList<Type>> interfaceTypes = + Suppliers.memoize( + new Supplier<ImmutableList<Type>>() { + @Override + public ImmutableList<Type> get() { + if (interfaces().isEmpty()) { + return ImmutableList.of(); + } + ImmutableList.Builder<Type> result = ImmutableList.builder(); + if (sig.get() == null || sig.get().interfaces() == null) { + for (ClassSymbol sym : interfaces()) { + result.add(ClassTy.asNonParametricClassTy(sym)); + } + } else { + Function<String, TyVarSymbol> scope = makeScope(env, sym, ImmutableMap.of()); + for (ClassTySig classTySig : sig.get().interfaces()) { + result.add(BytecodeBinder.bindClassTy(classTySig, scope)); + } + } + return result.build(); + } + }); + + @Override + public ImmutableList<Type> interfaceTypes() { + return interfaceTypes.get(); + } + + private final Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes = Suppliers.memoize( new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() { @Override @@ -292,15 +325,14 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou }); private static TyVarInfo bindTyParam(Sig.TyParamSig sig, Function<String, TyVarSymbol> scope) { - Type superClassBound = null; + ImmutableList.Builder<Type> bounds = ImmutableList.builder(); if (sig.classBound() != null) { - superClassBound = BytecodeBinder.bindTy(sig.classBound(), scope); + bounds.add(BytecodeBinder.bindTy(sig.classBound(), scope)); } - ImmutableList.Builder<Type> interfaceBounds = ImmutableList.builder(); for (Sig.TySig t : sig.interfaceBounds()) { - interfaceBounds.add(BytecodeBinder.bindTy(t, scope)); + bounds.add(BytecodeBinder.bindTy(t, scope)); } - return new TyVarInfo(superClassBound, interfaceBounds.build(), ImmutableList.of()); + return new TyVarInfo(IntersectionTy.create(bounds.build()), ImmutableList.of()); } @Override @@ -316,29 +348,14 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder(); for (ClassFile.FieldInfo cfi : classFile.get().fields()) { FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name()); - // we don't need a type here, the const value has all necessary information - Type type = null; + Type type = + BytecodeBinder.bindTy( + new SigParser(cfi.descriptor()).parseType(), + makeScope(env, sym, ImmutableMap.of())); int access = cfi.access(); Const.Value value = cfi.value(); if (value != null) { - // TODO(b/32626659): this is not bug-compatible with javac - switch (cfi.descriptor()) { - case "Z": - // boolean constants are encoded as integers - value = new Const.BooleanValue(value.asInteger().value() != 0); - break; - case "C": - value = new Const.CharValue(value.asChar().value()); - break; - case "S": - value = new Const.ShortValue(value.asShort().value()); - break; - case "B": - value = new Const.ByteValue(value.asByte().value()); - break; - default: - break; - } + value = BytecodeBinder.bindConstValue(type, value); } fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value)); } @@ -351,15 +368,11 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return fields.get(); } - Supplier<ImmutableList<MethodInfo>> methods = + private final Supplier<ImmutableList<MethodInfo>> methods = Suppliers.memoize( new Supplier<ImmutableList<MethodInfo>>() { @Override public ImmutableList<MethodInfo> get() { - // we only need methods for annotation declarations - if (kind() != TurbineTyKind.ANNOTATION) { - return ImmutableList.of(); - } ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); for (ClassFile.MethodInfo m : classFile.get().methods()) { methods.add(bindMethod(m)); @@ -372,9 +385,26 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou MethodSymbol methodSymbol = new MethodSymbol(sym, m.name()); Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig(); - verify(sig.tyParams().isEmpty()); + ImmutableMap<String, TyVarSymbol> tyParams; + { + ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder(); + for (Sig.TyParamSig p : sig.tyParams()) { + result.put(p.name(), new TyVarSymbol(methodSymbol, p.name())); + } + tyParams = result.build(); + } + + ImmutableMap<TyVarSymbol, TyVarInfo> tyParamTypes; + { + ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder(); + Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams); + for (Sig.TyParamSig p : sig.tyParams()) { + tparams.put(tyParams.get(p.name()), bindTyParam(p, scope)); + } + tyParamTypes = tparams.build(); + } - Function<String, TyVarSymbol> scope = makeScope(env, sym, ImmutableMap.of()); + Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams); Type ret = null; if (sig.returnType() != null) { @@ -382,23 +412,44 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder(); + int idx = 0; for (Sig.TySig tySig : sig.params()) { - formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), null, ImmutableList.of(), 0)); + String name = null; + int access = 0; + if (idx < m.parameters().size()) { + ParameterInfo paramInfo = m.parameters().get(idx); + name = paramInfo.name(); + access = paramInfo.access(); + } + ImmutableList<AnnoInfo> annotations = + (idx < m.parameterAnnotations().size()) + ? BytecodeBinder.bindAnnotations(m.parameterAnnotations().get(idx)) + : ImmutableList.of(); + formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), name, annotations, access)); + idx++; + } + + ImmutableList.Builder<Type> exceptions = ImmutableList.builder(); + for (TySig e : sig.exceptions()) { + exceptions.add(BytecodeBinder.bindTy(e, scope)); } - verify(sig.exceptions().isEmpty()); + Const defaultValue = + m.defaultValue() != null ? BytecodeBinder.bindValue(ret, m.defaultValue()) : null; + + ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations()); return new MethodInfo( methodSymbol, - ImmutableMap.of(), + tyParamTypes, ret, formals.build(), - ImmutableList.of(), + exceptions.build(), m.access(), - null /*defaultValue*/, - null /*decl*/, - ImmutableList.of(), - null); + defaultValue, + /* decl= */ null, + annotations, + /* receiver= */ null); } @Override @@ -406,7 +457,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return methods.get(); } - final Supplier<AnnotationMetadata> annotationMetadata = + private final Supplier<AnnotationMetadata> annotationMetadata = Suppliers.memoize( new Supplier<AnnotationMetadata>() { @Override @@ -415,7 +466,7 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return null; } RetentionPolicy retention = null; - ImmutableSet<ElementType> target = null; + ImmutableSet<TurbineElementType> target = null; ClassSymbol repeatable = null; for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) { switch (annotation.typeName()) { @@ -448,8 +499,8 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return RetentionPolicy.valueOf(enumVal.constName()); } - private static ImmutableSet<ElementType> bindTarget(AnnotationInfo annotation) { - ImmutableSet.Builder<ElementType> result = ImmutableSet.builder(); + private static ImmutableSet<TurbineElementType> bindTarget(AnnotationInfo annotation) { + ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder(); ElementValue val = annotation.elementValuePairs().get("value"); switch (val.kind()) { case ARRAY: @@ -469,9 +520,9 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou } private static void bindTargetElement( - ImmutableSet.Builder<ElementType> target, EnumConstValue enumVal) { + ImmutableSet.Builder<TurbineElementType> target, EnumConstValue enumVal) { if (enumVal.typeName().equals("Ljava/lang/annotation/ElementType;")) { - target.add(ElementType.valueOf(enumVal.constName())); + target.add(TurbineElementType.valueOf(enumVal.constName())); } } @@ -492,6 +543,20 @@ public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBou return annotationMetadata.get(); } + private final Supplier<ImmutableList<AnnoInfo>> annotations = + Suppliers.memoize( + new Supplier<ImmutableList<AnnoInfo>>() { + @Override + public ImmutableList<AnnoInfo> get() { + return BytecodeBinder.bindAnnotations(classFile.get().annotations()); + } + }); + + @Override + public ImmutableList<AnnoInfo> annotations() { + return annotations.get(); + } + /** * Create a scope for resolving type variable symbols declared in the class, and any enclosing * instances. diff --git a/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java b/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java index 1e6eee1..1e33d5f 100644 --- a/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java +++ b/java/com/google/turbine/binder/lookup/CanonicalSymbolResolver.java @@ -17,12 +17,13 @@ package com.google.turbine.binder.lookup; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.tree.Tree; /** Canonical type resolution. Breaks a circular dependency between binding and import handling. */ public interface CanonicalSymbolResolver extends ImportScope.ResolveFunction { /** Resolves a single member type of the given symbol by canonical name. */ @Override - ClassSymbol resolveOne(ClassSymbol sym, String bit); + ClassSymbol resolveOne(ClassSymbol sym, Tree.Ident bit); /** Returns true if the given symbol is visible from the current package. */ boolean visible(ClassSymbol sym); diff --git a/java/com/google/turbine/binder/lookup/ImportIndex.java b/java/com/google/turbine/binder/lookup/ImportIndex.java index eb3ac8f..f28d905 100644 --- a/java/com/google/turbine/binder/lookup/ImportIndex.java +++ b/java/com/google/turbine/binder/lookup/ImportIndex.java @@ -24,10 +24,10 @@ import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.sym.ClassSymbol; -import com.google.turbine.diag.SourceFile; -import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; +import com.google.turbine.diag.TurbineLog.TurbineLogWithSource; import com.google.turbine.tree.Tree; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.ImportDecl; import java.util.HashMap; import java.util.Map; @@ -49,13 +49,13 @@ public class ImportIndex implements ImportScope { */ private final Map<String, Supplier<ImportScope>> thunks; - public ImportIndex(ImmutableMap<String, Supplier<ImportScope>> thunks) { + public ImportIndex(TurbineLogWithSource log, ImmutableMap<String, Supplier<ImportScope>> thunks) { this.thunks = thunks; } /** Creates an import index for the given top-level environment. */ public static ImportIndex create( - SourceFile source, + TurbineLogWithSource log, CanonicalSymbolResolver resolve, final TopLevelIndex cpi, ImmutableList<ImportDecl> imports) { @@ -65,12 +65,12 @@ public class ImportIndex implements ImportScope { continue; } thunks.put( - getLast(i.type()), + getLast(i.type()).value(), Suppliers.memoize( new Supplier<ImportScope>() { @Override public ImportScope get() { - return namedImport(source, cpi, i, resolve); + return namedImport(log, cpi, i, resolve); } })); } @@ -80,37 +80,32 @@ public class ImportIndex implements ImportScope { if (!i.stat() || i.wild()) { continue; } - String last = getLast(i.type()); - if (thunks.containsKey(last)) { - continue; - } - thunks.put( + String last = getLast(i.type()).value(); + thunks.putIfAbsent( last, Suppliers.memoize( new Supplier<ImportScope>() { @Override public ImportScope get() { - return staticNamedImport(cpi, i); + return staticNamedImport(log, cpi, i); } })); } - return new ImportIndex(ImmutableMap.copyOf(thunks)); + return new ImportIndex(log, ImmutableMap.copyOf(thunks)); } /** Fully resolve the canonical name of a non-static named import. */ private static ImportScope namedImport( - SourceFile source, TopLevelIndex cpi, ImportDecl i, CanonicalSymbolResolver resolve) { + TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i, CanonicalSymbolResolver resolve) { LookupResult result = cpi.scope().lookup(new LookupKey(i.type())); if (result == null) { - throw TurbineError.format( - source, - i.position(), - ErrorKind.SYMBOL_NOT_FOUND, - new ClassSymbol(Joiner.on('/').join(i.type()))); + log.error( + i.position(), ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(Joiner.on('/').join(i.type()))); + return null; } ClassSymbol sym = (ClassSymbol) result.sym(); - for (String bit : result.remaining()) { - sym = resolveNext(source, i.position(), resolve, sym, bit); + for (Tree.Ident bit : result.remaining()) { + sym = resolveNext(log, i.position(), resolve, sym, bit); } ClassSymbol resolved = sym; return new ImportScope() { @@ -122,18 +117,15 @@ public class ImportIndex implements ImportScope { } private static ClassSymbol resolveNext( - SourceFile source, + TurbineLogWithSource log, int position, CanonicalSymbolResolver resolve, ClassSymbol sym, - String bit) { + Ident bit) { ClassSymbol next = resolve.resolveOne(sym, bit); if (next == null) { - throw TurbineError.format( - source, - position, - ErrorKind.SYMBOL_NOT_FOUND, - new ClassSymbol(sym.binaryName() + '$' + bit)); + log.error( + position, ErrorKind.SYMBOL_NOT_FOUND, new ClassSymbol(sym.binaryName() + '$' + bit)); } return next; } @@ -145,16 +137,18 @@ public class ImportIndex implements ImportScope { * hierarchy analysis is complete, so for now we resolve the base {@code java.util.HashMap} and * defer the rest. */ - private static ImportScope staticNamedImport(TopLevelIndex cpi, ImportDecl i) { + private static ImportScope staticNamedImport( + TurbineLogWithSource log, TopLevelIndex cpi, ImportDecl i) { LookupResult base = cpi.scope().lookup(new LookupKey(i.type())); if (base == null) { + log.error(i.position(), ErrorKind.SYMBOL_NOT_FOUND, Joiner.on(".").join(i.type())); return null; } return new ImportScope() { @Override public LookupResult lookup(LookupKey lookupKey, ResolveFunction resolve) { ClassSymbol sym = (ClassSymbol) base.sym(); - for (String bit : base.remaining()) { + for (Tree.Ident bit : base.remaining()) { sym = resolve.resolveOne(sym, bit); if (sym == null) { // Assume that static imports that don't resolve to types are non-type member imports, @@ -169,7 +163,7 @@ public class ImportIndex implements ImportScope { @Override public LookupResult lookup(LookupKey lookup, ResolveFunction resolve) { - Supplier<ImportScope> thunk = thunks.get(lookup.first()); + Supplier<ImportScope> thunk = thunks.get(lookup.first().value()); if (thunk == null) { return null; } diff --git a/java/com/google/turbine/binder/lookup/ImportScope.java b/java/com/google/turbine/binder/lookup/ImportScope.java index 8b28cb3..2e6917e 100644 --- a/java/com/google/turbine/binder/lookup/ImportScope.java +++ b/java/com/google/turbine/binder/lookup/ImportScope.java @@ -17,6 +17,7 @@ package com.google.turbine.binder.lookup; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.tree.Tree; /** * A scope for imports. Non-canonical imports depend on hierarchy analysis, so to break the cycle we @@ -31,7 +32,7 @@ public interface ImportScope { */ @FunctionalInterface interface ResolveFunction { - ClassSymbol resolveOne(ClassSymbol base, String name); + ClassSymbol resolveOne(ClassSymbol base, Tree.Ident name); } /** See {@link Scope#lookup(LookupKey)}. */ diff --git a/java/com/google/turbine/binder/lookup/LookupKey.java b/java/com/google/turbine/binder/lookup/LookupKey.java index 94e0838..1332fa1 100644 --- a/java/com/google/turbine/binder/lookup/LookupKey.java +++ b/java/com/google/turbine/binder/lookup/LookupKey.java @@ -18,6 +18,7 @@ package com.google.turbine.binder.lookup; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Immutable; +import com.google.turbine.tree.Tree.Ident; import java.util.NoSuchElementException; /** @@ -26,14 +27,14 @@ import java.util.NoSuchElementException; */ @Immutable public class LookupKey { - private final ImmutableList<String> simpleNames; + private final ImmutableList<Ident> simpleNames; - public LookupKey(Iterable<String> simpleNames) { - this.simpleNames = ImmutableList.copyOf(simpleNames); + public LookupKey(ImmutableList<Ident> simpleNames) { + this.simpleNames = simpleNames; } /** The first simple name in the qualified type name. */ - public String first() { + public Ident first() { return simpleNames.get(0); } @@ -62,7 +63,7 @@ public class LookupKey { } /** The simple names of the type. */ - public ImmutableList<String> simpleNames() { + public ImmutableList<Ident> simpleNames() { return simpleNames; } } diff --git a/java/com/google/turbine/binder/lookup/LookupResult.java b/java/com/google/turbine/binder/lookup/LookupResult.java index 0529ecd..7e0f737 100644 --- a/java/com/google/turbine/binder/lookup/LookupResult.java +++ b/java/com/google/turbine/binder/lookup/LookupResult.java @@ -20,6 +20,7 @@ import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.Immutable; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.Symbol; +import com.google.turbine.tree.Tree; /** * The result of a canonical type name lookup. @@ -36,16 +37,15 @@ public class LookupResult { } /** The remaining nested type names. */ - public ImmutableList<String> remaining() { + public ImmutableList<Tree.Ident> remaining() { return remaining; } private final Symbol sym; - private final ImmutableList<String> remaining; + private final ImmutableList<Tree.Ident> remaining; public LookupResult(Symbol sym, LookupKey remaining) { this.sym = sym; - this.remaining = - remaining.hasNext() ? remaining.rest().simpleNames() : ImmutableList.<String>of(); + this.remaining = remaining.hasNext() ? remaining.rest().simpleNames() : ImmutableList.of(); } } diff --git a/java/com/google/turbine/binder/lookup/MemberImportIndex.java b/java/com/google/turbine/binder/lookup/MemberImportIndex.java index 45eefb5..a8ecc7a 100644 --- a/java/com/google/turbine/binder/lookup/MemberImportIndex.java +++ b/java/com/google/turbine/binder/lookup/MemberImportIndex.java @@ -25,6 +25,7 @@ import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.diag.SourceFile; import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; +import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ImportDecl; import java.util.Iterator; import java.util.LinkedHashMap; @@ -59,7 +60,7 @@ public class MemberImportIndex { return null; } ClassSymbol sym = (ClassSymbol) result.sym(); - for (String bit : result.remaining()) { + for (Tree.Ident bit : result.remaining()) { sym = resolveNext(resolve, source, i.position(), sym, bit); } return sym; @@ -67,7 +68,7 @@ public class MemberImportIndex { })); } else { cache.put( - getLast(i.type()), + getLast(i.type()).value(), Suppliers.memoize( new Supplier<ClassSymbol>() { @Override @@ -93,7 +94,7 @@ public class MemberImportIndex { SourceFile source, int position, ClassSymbol sym, - String bit) { + Tree.Ident bit) { ClassSymbol next = resolve.resolveOne(sym, bit); if (next == null) { throw TurbineError.format( diff --git a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java index 697ce61..99aa464 100644 --- a/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java +++ b/java/com/google/turbine/binder/lookup/SimpleTopLevelIndex.java @@ -16,11 +16,9 @@ package com.google.turbine.binder.lookup; -import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.sym.ClassSymbol; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.Objects; import javax.annotation.Nullable; @@ -57,9 +55,8 @@ public class SimpleTopLevelIndex implements TopLevelIndex { * @return {@code null} if an existing symbol with the same name has already been inserted. */ private Node insert(String name, ClassSymbol sym) { - Node child; - if (children.containsKey(name)) { - child = children.get(name); + Node child = children.get(name); + if (child != null) { if (child.sym != null) { return null; } @@ -85,20 +82,26 @@ public class SimpleTopLevelIndex implements TopLevelIndex { /** Inserts a {@link ClassSymbol} into the index, creating any needed packages. */ public boolean insert(ClassSymbol sym) { - Iterator<String> it = Splitter.on('/').split(sym.binaryName()).iterator(); + String binaryName = sym.binaryName(); + int start = 0; + int end = binaryName.indexOf('/'); Node curr = root; - while (it.hasNext()) { - String simpleName = it.next(); - // if this is the last simple name in the qualified name of the top-level class being - // inserted, we are creating a node for the class symbol - ClassSymbol nodeSym = it.hasNext() ? null : sym; - curr = curr.insert(simpleName, nodeSym); + while (end != -1) { + String simpleName = binaryName.substring(start, end); + curr = curr.insert(simpleName, null); // If we've already inserted something with the current name (either a package or another // symbol), bail out. When inserting elements from the classpath, this results in the // expected first-match-wins semantics. - if (curr == null || !Objects.equals(curr.sym, nodeSym)) { + if (curr == null) { return false; } + start = end + 1; + end = binaryName.indexOf('/', start); + } + String simpleName = binaryName.substring(start); + curr = curr.insert(simpleName, sym); + if (curr == null || !Objects.equals(curr.sym, sym)) { + return false; } return true; } @@ -132,7 +135,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex { public LookupResult lookup(LookupKey lookupKey) { Node curr = root; while (true) { - curr = curr.lookup(lookupKey.first()); + curr = curr.lookup(lookupKey.first().value()); if (curr == null) { return null; } @@ -175,7 +178,7 @@ public class SimpleTopLevelIndex implements TopLevelIndex { @Override public LookupResult lookup(LookupKey lookupKey) { - Node result = node.lookup(lookupKey.first()); + Node result = node.lookup(lookupKey.first().value()); if (result != null && result.sym != null) { return new LookupResult(result.sym, lookupKey); } diff --git a/java/com/google/turbine/binder/lookup/WildImportIndex.java b/java/com/google/turbine/binder/lookup/WildImportIndex.java index 21e995d..043ccc5 100644 --- a/java/com/google/turbine/binder/lookup/WildImportIndex.java +++ b/java/com/google/turbine/binder/lookup/WildImportIndex.java @@ -20,6 +20,7 @@ import com.google.common.base.Supplier; import com.google.common.base.Suppliers; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ImportDecl; /** @@ -69,7 +70,11 @@ public class WildImportIndex implements ImportScope { TopLevelIndex cpi, ImportDecl i, final CanonicalSymbolResolver importResolver) { - Scope packageIndex = cpi.lookupPackage(i.type()); + ImmutableList.Builder<String> flatNames = ImmutableList.builder(); + for (Tree.Ident ident : i.type()) { + flatNames.add(ident.value()); + } + Scope packageIndex = cpi.lookupPackage(flatNames.build()); if (packageIndex != null) { // a wildcard import of a package return new ImportScope() { @@ -140,7 +145,7 @@ public class WildImportIndex implements ImportScope { ResolveFunction resolve, CanonicalSymbolResolver importResolver) { ClassSymbol member = (ClassSymbol) result.sym(); - for (String bit : result.remaining()) { + for (Tree.Ident bit : result.remaining()) { member = resolve.resolveOne(member, bit); if (member == null) { return null; diff --git a/java/com/google/turbine/bytecode/ClassReader.java b/java/com/google/turbine/bytecode/ClassReader.java index a9e2402..0070527 100644 --- a/java/com/google/turbine/bytecode/ClassReader.java +++ b/java/com/google/turbine/bytecode/ClassReader.java @@ -19,9 +19,13 @@ package com.google.turbine.bytecode; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.errorprone.annotations.CheckReturnValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo; import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.AnnotationValue; import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstClassValue; +import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue; import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue; +import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo; import com.google.turbine.bytecode.ClassFile.ModuleInfo; import com.google.turbine.bytecode.ClassFile.ModuleInfo.ExportInfo; import com.google.turbine.bytecode.ClassFile.ModuleInfo.OpenInfo; @@ -29,7 +33,6 @@ import com.google.turbine.bytecode.ClassFile.ModuleInfo.ProvideInfo; import com.google.turbine.bytecode.ClassFile.ModuleInfo.RequireInfo; import com.google.turbine.bytecode.ClassFile.ModuleInfo.UseInfo; import com.google.turbine.model.Const; -import com.google.turbine.model.TurbineFlag; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -74,7 +77,7 @@ public class ClassReader { } int minorVersion = reader.u2(); int majorVersion = reader.u2(); - if (majorVersion < 45 || majorVersion > 53) { + if (majorVersion < 45) { throw error("bad version: %d.%d", majorVersion, minorVersion); } ConstantPoolReader constantPool = ConstantPoolReader.readConstantPool(reader); @@ -99,15 +102,16 @@ public class ClassReader { String signature = null; List<ClassFile.InnerClass> innerclasses = Collections.emptyList(); - List<ClassFile.AnnotationInfo> annotations = Collections.emptyList(); + ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder(); ClassFile.ModuleInfo module = null; int attributesCount = reader.u2(); for (int j = 0; j < attributesCount; j++) { int attributeNameIndex = reader.u2(); String name = constantPool.utf8(attributeNameIndex); switch (name) { + case "RuntimeInvisibleAnnotations": case "RuntimeVisibleAnnotations": - annotations = readAnnotations(constantPool, accessFlags); + readAnnotations(annotations, constantPool); break; case "Signature": signature = readSignature(constantPool); @@ -132,7 +136,7 @@ public class ClassReader { interfaces, methodinfos, fieldinfos, - annotations, + annotations.build(), innerclasses, ImmutableList.of(), module); @@ -175,22 +179,42 @@ public class ClassReader { * <p>The only annotations that affect header compilation are {@link @Retention} and * {@link @Target} on annotation declarations. */ - private List<ClassFile.AnnotationInfo> readAnnotations( - ConstantPoolReader constantPool, int accessFlags) { - List<ClassFile.AnnotationInfo> annotations = new ArrayList<>(); - if ((accessFlags & TurbineFlag.ACC_ANNOTATION) == 0) { - reader.skip(reader.u4()); - return ImmutableList.of(); - } + private void readAnnotations( + ImmutableList.Builder<ClassFile.AnnotationInfo> annotations, + ConstantPoolReader constantPool) { reader.u4(); // length int numAnnotations = reader.u2(); for (int n = 0; n < numAnnotations; n++) { - ClassFile.AnnotationInfo tmp = readAnnotation(constantPool); - if (tmp != null) { - annotations.add(tmp); + annotations.add(readAnnotation(constantPool)); + } + } + + /** Processes a JVMS 4.7.18 RuntimeVisibleParameterAnnotations attribute */ + public void readParameterAnnotations( + List<ImmutableList.Builder<AnnotationInfo>> annotations, ConstantPoolReader constantPool) { + reader.u4(); // length + int numParameters = reader.u1(); + while (annotations.size() < numParameters) { + annotations.add(ImmutableList.builder()); + } + for (int i = 0; i < numParameters; i++) { + int numAnnotations = reader.u2(); + for (int n = 0; n < numAnnotations; n++) { + annotations.get(i).add(readAnnotation(constantPool)); } } - return annotations; + } + + /** Processes a JVMS 4.7.24 MethodParameters attribute. */ + private void readMethodParameters( + ImmutableList.Builder<ParameterInfo> parameters, ConstantPoolReader constantPool) { + reader.u4(); // length + int numParameters = reader.u1(); + for (int i = 0; i < numParameters; i++) { + String name = constantPool.utf8(reader.u2()); + int access = reader.u2(); + parameters.add(new ParameterInfo(name, access)); + } } /** Processes a JVMS 4.7.25 Module attribute. */ @@ -278,36 +302,23 @@ public class ClassReader { private ClassFile.AnnotationInfo readAnnotation(ConstantPoolReader constantPool) { int typeIndex = reader.u2(); String annotationType = constantPool.utf8(typeIndex); - boolean read; - switch (annotationType) { - case "Ljava/lang/annotation/Retention;": - case "Ljava/lang/annotation/Target;": - case "Ljava/lang/annotation/Repeatable;": - read = true; - break; - default: - read = false; - break; - } int numElementValuePairs = reader.u2(); - ClassFile.AnnotationInfo result = null; + ImmutableMap.Builder<String, ElementValue> values = ImmutableMap.builder(); for (int e = 0; e < numElementValuePairs; e++) { int elementNameIndex = reader.u2(); String key = constantPool.utf8(elementNameIndex); - boolean value = read && key.equals("value"); - ElementValue tmp = readElementValue(constantPool, value); - if (tmp != null) { - result = new ClassFile.AnnotationInfo(annotationType, true, ImmutableMap.of(key, tmp)); - } + ElementValue value = readElementValue(constantPool); + values.put(key, value); } - return result; + return new ClassFile.AnnotationInfo( + annotationType, + // The runtimeVisible bit in AnnotationInfo is only used during lowering; earlier passes + // read the meta-annotations. + /* runtimeVisible= */ false, + values.build()); } - /** - * Extracts the value of an annotation declaration meta-annotation, or else skips over the element - * value pair. - */ - private ElementValue readElementValue(ConstantPoolReader constantPool, boolean value) { + private ElementValue readElementValue(ConstantPoolReader constantPool) { int tag = reader.u1(); switch (tag) { case 'B': @@ -319,52 +330,38 @@ public class ClassReader { case 'S': case 'Z': case 's': - reader.u2(); // constValueIndex - break; + { + int constValueIndex = reader.u2(); + return new ConstValue(constantPool.constant(constValueIndex)); + } case 'e': { int typeNameIndex = reader.u2(); int constNameIndex = reader.u2(); - if (value) { - String typeName = constantPool.utf8(typeNameIndex); - switch (typeName) { - case "Ljava/lang/annotation/RetentionPolicy;": - case "Ljava/lang/annotation/ElementType;": - String constName = constantPool.utf8(constNameIndex); - return new EnumConstValue(typeName, constName); - default: - break; - } - } - break; + String typeName = constantPool.utf8(typeNameIndex); + String constName = constantPool.utf8(constNameIndex); + return new EnumConstValue(typeName, constName); } case 'c': - int classInfoIndex = reader.u2(); - String className = constantPool.utf8(classInfoIndex); - return new ConstClassValue(className); + { + int classInfoIndex = reader.u2(); + String className = constantPool.utf8(classInfoIndex); + return new ConstClassValue(className); + } case '@': - readAnnotation(constantPool); - break; + return new AnnotationValue(readAnnotation(constantPool)); case '[': { int numValues = reader.u2(); - if (value) { - ImmutableList.Builder<ElementValue> elements = ImmutableList.builder(); - for (int i = 0; i < numValues; i++) { - elements.add(readElementValue(constantPool, true)); - } - return new ElementValue.ArrayValue(elements.build()); - } else { - for (int i = 0; i < numValues; i++) { - readElementValue(constantPool, false); - } + ImmutableList.Builder<ElementValue> elements = ImmutableList.builder(); + for (int i = 0; i < numValues; i++) { + elements.add(readElementValue(constantPool)); } - break; + return new ElementValue.ArrayValue(elements.build()); } - default: - throw error("bad tag value %c", tag); + default: // fall out } - return null; + throw new AssertionError(String.format("bad tag value %c", tag)); } /** Reads JVMS 4.6 method_infos. */ @@ -380,6 +377,11 @@ public class ClassReader { int attributesCount = reader.u2(); String signature = null; ImmutableList<String> exceptions = ImmutableList.of(); + ImmutableList.Builder<ClassFile.AnnotationInfo> annotations = ImmutableList.builder(); + List<ImmutableList.Builder<ClassFile.AnnotationInfo>> parameterAnnotationsBuilder = + new ArrayList<>(); + ImmutableList.Builder<ParameterInfo> parameters = ImmutableList.builder(); + ElementValue defaultValue = null; for (int j = 0; j < attributesCount; j++) { String attributeName = constantPool.utf8(reader.u2()); switch (attributeName) { @@ -389,11 +391,31 @@ public class ClassReader { case "Signature": signature = readSignature(constantPool); break; + case "AnnotationDefault": + reader.u4(); // length + defaultValue = readElementValue(constantPool); + break; + case "RuntimeInvisibleAnnotations": + case "RuntimeVisibleAnnotations": + readAnnotations(annotations, constantPool); + break; + case "RuntimeInvisibleParameterAnnotations": + case "RuntimeVisibleParameterAnnotations": + readParameterAnnotations(parameterAnnotationsBuilder, constantPool); + break; + case "MethodParameters": + readMethodParameters(parameters, constantPool); + break; default: reader.skip(reader.u4()); break; } } + ImmutableList.Builder<ImmutableList<AnnotationInfo>> parameterAnnotations = + ImmutableList.builder(); + for (ImmutableList.Builder<AnnotationInfo> x : parameterAnnotationsBuilder) { + parameterAnnotations.add(x.build()); + } methods.add( new ClassFile.MethodInfo( accessFlags, @@ -401,11 +423,11 @@ public class ClassReader { desc, signature, exceptions, - null, - ImmutableList.of(), - ImmutableList.of(), - ImmutableList.of(), - ImmutableList.of())); + defaultValue, + annotations.build(), + parameterAnnotations.build(), + /* typeAnnotations= */ ImmutableList.of(), + parameters.build())); } return methods; } diff --git a/java/com/google/turbine/bytecode/ConstantPoolReader.java b/java/com/google/turbine/bytecode/ConstantPoolReader.java index b6a2091..ffcb4c3 100644 --- a/java/com/google/turbine/bytecode/ConstantPoolReader.java +++ b/java/com/google/turbine/bytecode/ConstantPoolReader.java @@ -163,6 +163,8 @@ public class ConstantPoolReader { return new Const.IntValue(reader.readInt()); case CONSTANT_STRING: return new Const.StringValue(utf8(reader.readUnsignedShort())); + case CONSTANT_UTF8: + return new Const.StringValue(reader.readUTF()); default: throw new AssertionError(String.format("bad tag: %x", tag)); } diff --git a/java/com/google/turbine/bytecode/sig/SigParser.java b/java/com/google/turbine/bytecode/sig/SigParser.java index 1db0dce..033fa18 100644 --- a/java/com/google/turbine/bytecode/sig/SigParser.java +++ b/java/com/google/turbine/bytecode/sig/SigParser.java @@ -145,7 +145,8 @@ public class SigParser { return tyParams.build(); } - private TySig parseType() { + /** Parses a type signature. */ + public TySig parseType() { switch (peek()) { case 'Z': eat(); diff --git a/java/com/google/turbine/deps/Dependencies.java b/java/com/google/turbine/deps/Dependencies.java index a3c654e..f76efe7 100644 --- a/java/com/google/turbine/deps/Dependencies.java +++ b/java/com/google/turbine/deps/Dependencies.java @@ -16,7 +16,6 @@ package com.google.turbine.deps; -import com.google.common.base.Optional; import com.google.common.base.Predicates; import com.google.common.collect.Collections2; import com.google.common.collect.ImmutableList; @@ -40,6 +39,7 @@ import java.nio.file.Paths; import java.util.Collection; import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.Optional; import java.util.Set; /** Support for Bazel jdeps dependency output. */ diff --git a/java/com/google/turbine/diag/TurbineDiagnostic.java b/java/com/google/turbine/diag/TurbineDiagnostic.java new file mode 100644 index 0000000..0404a8e --- /dev/null +++ b/java/com/google/turbine/diag/TurbineDiagnostic.java @@ -0,0 +1,128 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.diag; + +import static com.google.common.base.MoreObjects.firstNonNull; +import static com.google.common.base.Preconditions.checkArgument; +import static com.google.common.collect.Iterables.getOnlyElement; +import static java.util.Objects.requireNonNull; + +import com.google.common.base.CharMatcher; +import com.google.common.base.Strings; +import com.google.common.collect.ImmutableList; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.diag.TurbineError.ErrorKind; +import java.util.Objects; + +/** A compilation error. */ +public class TurbineDiagnostic { + + private final ErrorKind kind; + private final String diagnostic; + private final ImmutableList<Object> args; + + private TurbineDiagnostic(ErrorKind kind, String diagnostic, ImmutableList<Object> args) { + this.kind = requireNonNull(kind); + this.diagnostic = requireNonNull(diagnostic); + this.args = requireNonNull(args); + } + + /** The diagnostic kind. */ + public ErrorKind kind() { + return kind; + } + + /** The diagnostic message. */ + public String diagnostic() { + return diagnostic; + } + + /** The diagnostic arguments. */ + public ImmutableList<Object> args() { + return args; + } + + private static TurbineDiagnostic create( + ErrorKind kind, String diagnostic, ImmutableList<Object> args) { + switch (kind) { + case SYMBOL_NOT_FOUND: + { + checkArgument( + args.size() == 1 && getOnlyElement(args) instanceof ClassSymbol, + "diagnostic (%s) has invalid argument args %s", + diagnostic, + args); + break; + } + default: // fall out + } + return new TurbineDiagnostic(kind, diagnostic, args); + } + + /** + * Formats a diagnostic. + * + * @param source the current source file + * @param kind the error kind + * @param args format args + */ + public static TurbineDiagnostic format(SourceFile source, ErrorKind kind, Object... args) { + String path = firstNonNull(source.path(), "<>"); + String message = kind.format(args); + String diagnostic = path + ": error: " + message.trim() + System.lineSeparator(); + return create(kind, diagnostic, ImmutableList.copyOf(args)); + } + + /** + * Formats a diagnostic. + * + * @param position the diagnostic position + * @param kind the error kind + * @param args format args + */ + public static TurbineDiagnostic format( + SourceFile source, int position, ErrorKind kind, Object... args) { + String path = firstNonNull(source.path(), "<>"); + LineMap lineMap = LineMap.create(source.source()); + int lineNumber = lineMap.lineNumber(position); + int column = lineMap.column(position); + String message = kind.format(args); + + StringBuilder sb = new StringBuilder(path).append(":"); + sb.append(lineNumber).append(": error: "); + sb.append(message.trim()).append(System.lineSeparator()); + sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lineMap.line(position))) + .append(System.lineSeparator()); + sb.append(Strings.repeat(" ", column)).append('^'); + String diagnostic = sb.toString(); + return create(kind, diagnostic, ImmutableList.copyOf(args)); + } + + @Override + public int hashCode() { + return Objects.hash(diagnostic, kind); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof TurbineDiagnostic)) { + return false; + } + TurbineDiagnostic that = (TurbineDiagnostic) obj; + return diagnostic.equals(that.diagnostic) && kind.equals(that.kind); + } +} diff --git a/java/com/google/turbine/diag/TurbineError.java b/java/com/google/turbine/diag/TurbineError.java index cd7d9e1..e13eb44 100644 --- a/java/com/google/turbine/diag/TurbineError.java +++ b/java/com/google/turbine/diag/TurbineError.java @@ -16,14 +16,9 @@ package com.google.turbine.diag; -import static com.google.common.base.MoreObjects.firstNonNull; -import static com.google.common.base.Preconditions.checkArgument; -import static com.google.common.collect.Iterables.getOnlyElement; +import static java.util.stream.Collectors.joining; -import com.google.common.base.CharMatcher; -import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; -import com.google.turbine.binder.sym.ClassSymbol; /** A compilation error. */ public class TurbineError extends Error { @@ -33,6 +28,9 @@ public class TurbineError extends Error { UNEXPECTED_INPUT("unexpected input: %c"), UNEXPECTED_IDENTIFIER("unexpected identifier '%s'"), UNEXPECTED_EOF("unexpected end of input"), + UNTERMINATED_STRING("unterminated string literal"), + UNTERMINATED_CHARACTER_LITERAL("unterminated char literal"), + EMPTY_CHARACTER_LITERAL("empty char literal"), EXPECTED_TOKEN("expected token %s"), INVALID_LITERAL("invalid literal: %s"), UNEXPECTED_TYPE_PARAMETER("unexpected type parameter %s"), @@ -68,10 +66,7 @@ public class TurbineError extends Error { * @param args format args */ public static TurbineError format(SourceFile source, ErrorKind kind, Object... args) { - String path = firstNonNull(source.path(), "<>"); - String message = kind.format(args); - String diagnostic = path + ": error: " + message.trim() + System.lineSeparator(); - return new TurbineError(kind, diagnostic, ImmutableList.copyOf(args)); + return new TurbineError(ImmutableList.of(TurbineDiagnostic.format(source, kind, args))); } /** @@ -83,50 +78,18 @@ public class TurbineError extends Error { */ public static TurbineError format( SourceFile source, int position, ErrorKind kind, Object... args) { - String path = firstNonNull(source.path(), "<>"); - LineMap lineMap = LineMap.create(source.source()); - int lineNumber = lineMap.lineNumber(position); - int column = lineMap.column(position); - String message = kind.format(args); - - StringBuilder sb = new StringBuilder(path).append(":"); - sb.append(lineNumber).append(": error: "); - sb.append(message.trim()).append(System.lineSeparator()); - sb.append(CharMatcher.breakingWhitespace().trimTrailingFrom(lineMap.line(position))) - .append(System.lineSeparator()); - sb.append(Strings.repeat(" ", column)).append('^'); - String diagnostic = sb.toString(); - return new TurbineError(kind, diagnostic, ImmutableList.copyOf(args)); + return new TurbineError( + ImmutableList.of(TurbineDiagnostic.format(source, position, kind, args))); } - private final ErrorKind kind; - private final ImmutableList<Object> args; - - private TurbineError(ErrorKind kind, String diagnostic, ImmutableList<Object> args) { - super(diagnostic); - switch (kind) { - case SYMBOL_NOT_FOUND: - { - checkArgument( - args.size() == 1 && getOnlyElement(args) instanceof ClassSymbol, - "diagnostic (%s) has invalid argument args %s", - diagnostic, - args); - break; - } - default: // fall out - } - this.kind = kind; - this.args = args; - } + private final ImmutableList<TurbineDiagnostic> diagnostics; - /** The diagnostic kind. */ - public ErrorKind kind() { - return kind; + public TurbineError(ImmutableList<TurbineDiagnostic> diagnostics) { + super(diagnostics.stream().map(d -> d.diagnostic()).collect(joining("\n"))); + this.diagnostics = diagnostics; } - /** The diagnostic arguments. */ - public ImmutableList<Object> args() { - return args; + public ImmutableList<TurbineDiagnostic> diagnostics() { + return diagnostics; } } diff --git a/java/com/google/turbine/diag/TurbineLog.java b/java/com/google/turbine/diag/TurbineLog.java new file mode 100644 index 0000000..fd8fc38 --- /dev/null +++ b/java/com/google/turbine/diag/TurbineLog.java @@ -0,0 +1,56 @@ +/* + * Copyright 2016 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.diag; + +import com.google.common.collect.ImmutableList; +import com.google.turbine.diag.TurbineError.ErrorKind; +import java.util.LinkedHashSet; +import java.util.Set; + +/** A log that collects diagnostics. */ +public class TurbineLog { + + private final Set<TurbineDiagnostic> errors = new LinkedHashSet<>(); + + public TurbineLogWithSource withSource(SourceFile source) { + return new TurbineLogWithSource(source); + } + + public void maybeThrow() { + if (!errors.isEmpty()) { + throw new TurbineError(ImmutableList.copyOf(errors)); + } + } + + /** A log for a specific source file. */ + public class TurbineLogWithSource { + + private final SourceFile source; + + private TurbineLogWithSource(SourceFile source) { + this.source = source; + } + + public void error(ErrorKind kind, Object... args) { + errors.add(TurbineDiagnostic.format(source, kind, args)); + } + + public void error(int position, ErrorKind kind, Object... args) { + errors.add(TurbineDiagnostic.format(source, position, kind, args)); + } + } +} diff --git a/java/com/google/turbine/lower/Lower.java b/java/com/google/turbine/lower/Lower.java index 04f352e..99b5627 100644 --- a/java/com/google/turbine/lower/Lower.java +++ b/java/com/google/turbine/lower/Lower.java @@ -65,12 +65,14 @@ import com.google.turbine.diag.TurbineError; import com.google.turbine.diag.TurbineError.ErrorKind; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineFlag; +import com.google.turbine.model.TurbineTyKind; import com.google.turbine.model.TurbineVisibility; import com.google.turbine.type.AnnoInfo; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.TyKind; import com.google.turbine.type.Type.TyVar; import com.google.turbine.type.Type.WildTy; import com.google.turbine.types.Erasure; @@ -236,7 +238,7 @@ public class Lower { private byte[] lower(SourceTypeBoundClass info, ClassSymbol sym, Set<ClassSymbol> symbols) { int access = classAccess(info); String name = sig.descriptor(sym); - String signature = sig.classSignature(info); + String signature = sig.classSignature(info, env); String superName = info.superclass() != null ? sig.descriptor(info.superclass()) : null; List<String> interfaces = new ArrayList<>(); for (ClassSymbol i : info.interfaces()) { @@ -672,15 +674,12 @@ public class Lower { TypePath.root(), info)); } - if (p.superClassBound() != null) { - lowerTypeAnnotations( - result, - p.superClassBound(), - boundTargetType, - new TypeAnnotationInfo.TypeParameterBoundTarget(typeParameterIndex, 0)); - } - int boundIndex = 1; // super class bound index is always 0; interface bounds start at 1 - for (Type i : p.interfaceBounds()) { + int boundIndex = 0; + for (Type i : p.bound().bounds()) { + if (boundIndex == 0 && isInterface(i, env)) { + // super class bound index is always 0; interface bounds start at 1 + boundIndex++; + } lowerTypeAnnotations( result, i, @@ -691,6 +690,11 @@ public class Lower { } } + private boolean isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env) { + return type.tyKind() == TyKind.CLASS_TY + && env.get(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE; + } + private void lowerTypeAnnotations( Builder<TypeAnnotationInfo> result, Type type, TargetType targetType, Target target) { new LowerTypeAnnotations(result, targetType, target) @@ -781,7 +785,7 @@ public class Lower { } private void lowerClassTypeTypeAnnotations(ClassTy type, TypePath path) { - for (SimpleClassTy simple : type.classes) { + for (SimpleClassTy simple : type.classes()) { lowerTypeAnnotations(simple.annos(), path); int idx = 0; for (Type a : simple.targs()) { diff --git a/java/com/google/turbine/lower/LowerSignature.java b/java/com/google/turbine/lower/LowerSignature.java index 5fe3548..fe9b912 100644 --- a/java/com/google/turbine/lower/LowerSignature.java +++ b/java/com/google/turbine/lower/LowerSignature.java @@ -19,6 +19,7 @@ package com.google.turbine.lower; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.bound.SourceTypeBoundClass; import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; @@ -32,11 +33,13 @@ import com.google.turbine.bytecode.sig.Sig.TySig; import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig; import com.google.turbine.bytecode.sig.SigWriter; import com.google.turbine.model.TurbineFlag; +import com.google.turbine.model.TurbineTyKind; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; import com.google.turbine.type.Type.PrimTy; +import com.google.turbine.type.Type.TyKind; import com.google.turbine.type.Type.TyVar; import com.google.turbine.type.Type.WildTy; import java.util.Iterator; @@ -84,7 +87,7 @@ public class LowerSignature { private ClassTySig classTySig(ClassTy t) { classes.add(t.sym()); ImmutableList.Builder<SimpleClassTySig> classes = ImmutableList.builder(); - Iterator<SimpleClassTy> it = t.classes.iterator(); + Iterator<SimpleClassTy> it = t.classes().iterator(); SimpleClassTy curr = it.next(); while (curr.targs().isEmpty() && it.hasNext()) { curr = it.next(); @@ -141,7 +144,7 @@ public class LowerSignature { if (!needsMethodSig(sym, env, method)) { return null; } - ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams()); + ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams(), env); ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder(); for (SourceTypeBoundClass.ParamInfo t : method.parameters()) { if (t.synthetic()) { @@ -203,19 +206,19 @@ public class LowerSignature { * Produces a class signature attribute for a generic class, or {@code null} if the signature is * unnecessary. */ - public String classSignature(SourceTypeBoundClass info) { + public String classSignature(SourceTypeBoundClass info, Env<ClassSymbol, TypeBoundClass> env) { if (!classNeedsSig(info)) { return null; } - ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes()); + ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes(), env); ClassTySig xtnd = null; if (info.superClassType() != null) { - xtnd = classTySig(info.superClassType()); + xtnd = classTySig((ClassTy) info.superClassType()); } ImmutableList.Builder<ClassTySig> impl = ImmutableList.builder(); - for (ClassTy i : info.interfaceTypes()) { - impl.add(classTySig(i)); + for (Type i : info.interfaceTypes()) { + impl.add(classTySig((ClassTy) i)); } ClassSig sig = new ClassSig(typarams, xtnd, impl.build()); return SigWriter.classSig(sig); @@ -235,7 +238,7 @@ public class LowerSignature { if (ci.superClassType() != null && needsSig(ci.superClassType())) { return true; } - for (ClassTy i : ci.interfaceTypes()) { + for (Type i : ci.interfaceTypes()) { if (needsSig(i)) { return true; } @@ -250,7 +253,7 @@ public class LowerSignature { return false; case CLASS_TY: { - for (SimpleClassTy s : ((ClassTy) ty).classes) { + for (SimpleClassTy s : ((ClassTy) ty).classes()) { if (!s.targs().isEmpty()) { return true; } @@ -267,31 +270,46 @@ public class LowerSignature { } private ImmutableList<Sig.TyParamSig> tyParamSig( - Map<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> px) { + Map<TyVarSymbol, TyVarInfo> px, Env<ClassSymbol, TypeBoundClass> env) { ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder(); for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) { - result.add(tyParamSig(entry.getKey(), entry.getValue())); + result.add(tyParamSig(entry.getKey(), entry.getValue(), env)); } return result.build(); } - private Sig.TyParamSig tyParamSig(TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info) { + private Sig.TyParamSig tyParamSig( + TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) { + String identifier = sym.name(); Sig.TySig cbound = null; - if (info.superClassBound() != null) { - cbound = signature(info.superClassBound()); - } else if (info.interfaceBounds().isEmpty()) { + ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder(); + if (info.bound().bounds().isEmpty()) { cbound = new ClassTySig( "java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of()))); - } - ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder(); - for (Type i : info.interfaceBounds()) { - ibounds.add(signature(i)); + } else { + boolean first = true; + for (Type bound : info.bound().bounds()) { + TySig sig = signature(bound); + if (first) { + if (!isInterface(bound, env)) { + cbound = sig; + continue; + } + } + ibounds.add(sig); + first = false; + } } return new Sig.TyParamSig(identifier, cbound, ibounds.build()); } + private boolean isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env) { + return type.tyKind() == TyKind.CLASS_TY + && env.get(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE; + } + public String descriptor(ClassSymbol sym) { classes.add(sym); return sym.binaryName(); diff --git a/java/com/google/turbine/main/Main.java b/java/com/google/turbine/main/Main.java index 31a36c4..03a68ce 100644 --- a/java/com/google/turbine/main/Main.java +++ b/java/com/google/turbine/main/Main.java @@ -18,7 +18,6 @@ package com.google.turbine.main; import static java.nio.charset.StandardCharsets.UTF_8; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.hash.Hashing; import com.google.turbine.binder.Binder; @@ -30,6 +29,7 @@ import com.google.turbine.binder.JimageClassBinder; import com.google.turbine.deps.Dependencies; import com.google.turbine.deps.Transitive; import com.google.turbine.diag.SourceFile; +import com.google.turbine.diag.TurbineError; import com.google.turbine.lower.Lower; import com.google.turbine.lower.Lower.Lowered; import com.google.turbine.options.TurbineOptions; @@ -50,6 +50,7 @@ import java.time.ZoneId; import java.util.Arrays; import java.util.Collection; import java.util.Map; +import java.util.Optional; import java.util.jar.Attributes; import java.util.jar.JarEntry; import java.util.jar.JarFile; @@ -70,7 +71,17 @@ public class Main { static final Attributes.Name INJECTING_RULE_KIND = new Attributes.Name("Injecting-Rule-Kind"); public static void main(String[] args) throws IOException { - compile(args); + boolean ok; + try { + ok = compile(args); + } catch (TurbineError | UsageException e) { + System.err.println(e.getMessage()); + ok = false; + } catch (Throwable turbineCrash) { + turbineCrash.printStackTrace(); + ok = false; + } + System.exit(ok ? 0 : 1); } public static boolean compile(String[] args) throws IOException { @@ -79,9 +90,7 @@ public class Main { } public static boolean compile(TurbineOptions options) throws IOException { - if (!options.processors().isEmpty()) { - return false; - } + usage(options); ImmutableList<CompUnit> units = parseAll(options); @@ -93,7 +102,7 @@ public class Main { ClassPath classpath = ClassPathBinder.bindClasspath(toPaths(reducedClasspath)); BindingResult bound = - Binder.bind(units, classpath, bootclasspath, /* moduleVersion=*/ Optional.absent()); + Binder.bind(units, classpath, bootclasspath, /* moduleVersion=*/ Optional.empty()); // TODO(cushon): parallelize Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()); @@ -113,10 +122,25 @@ public class Main { return true; } + private static void usage(TurbineOptions options) { + if (!options.processors().isEmpty()) { + throw new UsageException("--processors is not supported"); + } + if (options.sources().isEmpty() && options.sourceJars().isEmpty()) { + throw new UsageException("no sources were provided"); + } + if (options.help()) { + throw new UsageException(); + } + if (!options.output().isPresent()) { + throw new UsageException("--output is required"); + } + } + private static ClassPath bootclasspath(TurbineOptions options) throws IOException { // if both --release and --bootclasspath are specified, --release wins if (options.release().isPresent() && options.system().isPresent()) { - throw new IllegalArgumentException("expected at most one of --release and --system"); + throw new UsageException("expected at most one of --release and --system"); } if (options.release().isPresent()) { @@ -128,7 +152,7 @@ public class Main { // ... otherwise, search ct.sym for a matching release ClassPath bootclasspath = CtSymClassBinder.bind(release); if (bootclasspath == null) { - throw new IllegalArgumentException("not a supported release: " + release); + throw new UsageException("not a supported release: " + release); } return bootclasspath; } @@ -166,7 +190,7 @@ public class Main { private static void writeOutput( TurbineOptions options, Map<String, byte[]> lowered, Map<String, byte[]> transitive) throws IOException { - Path path = Paths.get(options.outputFile()); + Path path = Paths.get(options.output().get()); try (OutputStream os = Files.newOutputStream(path); BufferedOutputStream bos = new BufferedOutputStream(os, BUFFER_SIZE); JarOutputStream jos = new JarOutputStream(bos)) { diff --git a/java/com/google/turbine/main/UsageException.java b/java/com/google/turbine/main/UsageException.java new file mode 100644 index 0000000..bd1ecba --- /dev/null +++ b/java/com/google/turbine/main/UsageException.java @@ -0,0 +1,64 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.main; + +import static java.util.Objects.requireNonNull; + +import com.google.common.base.Joiner; + +/** A command-line usage error. */ +class UsageException extends RuntimeException { + + private static final String[] USAGE = { + "", + "Usage: turbine [options]", + "", + "Options:", + " --output", + " The jar output file.", + " --sources", + " The sources to compile.", + " --source_jars", + " jar archives of sources to compile.", + " --classpath", + " The compilation classpath.", + " --bootclasspath", + " The compilation bootclasspath.", + " --help", + " Print this usage statement.", + " @<filename>", + " Read options and filenames from file.", + "", + }; + + UsageException() { + super(buildMessage(null)); + } + + UsageException(String message) { + super(buildMessage(requireNonNull(message))); + } + + private static String buildMessage(String message) { + StringBuilder builder = new StringBuilder(); + if (message != null) { + builder.append(message).append('\n'); + } + Joiner.on('\n').appendTo(builder, USAGE).append('\n'); + return builder.toString(); + } +} diff --git a/java/com/google/turbine/model/Const.java b/java/com/google/turbine/model/Const.java index f683f38..6e41bd2 100644 --- a/java/com/google/turbine/model/Const.java +++ b/java/com/google/turbine/model/Const.java @@ -16,6 +16,7 @@ package com.google.turbine.model; +import com.google.common.base.Joiner; import com.google.common.collect.ImmutableList; /** @@ -24,6 +25,12 @@ import com.google.common.collect.ImmutableList; */ public abstract class Const { + @Override + public abstract int hashCode(); + + @Override + public abstract boolean equals(Object obj); + /** The constant kind. */ public abstract Kind kind(); @@ -36,6 +43,13 @@ public abstract class Const { ANNOTATION } + /** An invalid constant cast. */ + public static class ConstCastError extends RuntimeException { + public ConstCastError(TurbineConstantTypeKind type, TurbineConstantTypeKind target) { + super(String.format("%s cannot be converted to %s", type, target)); + } + } + /** Subtypes of {@link Const} for primitive and String literals. */ public abstract static class Value extends Const { public abstract TurbineConstantTypeKind constantTypeKind(); @@ -46,39 +60,39 @@ public abstract class Const { } public IntValue asInteger() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.INT); } public FloatValue asFloat() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.FLOAT); } public DoubleValue asDouble() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.DOUBLE); } public LongValue asLong() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.LONG); } public BooleanValue asBoolean() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.BOOLEAN); } public StringValue asString() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.STRING); } public CharValue asChar() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.CHAR); } public ShortValue asShort() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.SHORT); } public ByteValue asByte() { - throw new AssertionError(constantTypeKind()); + throw new ConstCastError(constantTypeKind(), TurbineConstantTypeKind.BYTE); } } @@ -113,6 +127,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Boolean.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof BooleanValue && value == ((BooleanValue) obj).value(); + } } /** An int literal value. */ @@ -177,6 +201,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Integer.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof IntValue && value == ((IntValue) obj).value; + } } /** A long literal value. */ @@ -240,6 +274,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Long.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof LongValue && value == ((LongValue) obj).value; + } } /** A char literal value. */ @@ -303,6 +347,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Character.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof CharValue && value == ((CharValue) obj).value; + } } /** A float literal value. */ @@ -366,6 +420,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Float.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof FloatValue && value == ((FloatValue) obj).value; + } } /** A double literal value. */ @@ -429,6 +493,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Double.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof DoubleValue && value == ((DoubleValue) obj).value; + } } /** A String literal value. */ @@ -457,6 +531,16 @@ public abstract class Const { public StringValue asString() { return this; } + + @Override + public int hashCode() { + return value.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof StringValue && value.equals(((StringValue) obj).value); + } } /** A short literal value. */ @@ -520,6 +604,16 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Short.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ShortValue && value == ((ShortValue) obj).value; + } } /** A byte literal value. */ @@ -579,6 +673,21 @@ public abstract class Const { public StringValue asString() { return new StringValue(String.valueOf(value)); } + + @Override + public int hashCode() { + return Byte.hashCode(value); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ByteValue && value == ((ByteValue) obj).value; + } + + @Override + public String toString() { + return String.valueOf(value); + } } /** A constant array literal (e.g. in an annotation). */ @@ -598,5 +707,20 @@ public abstract class Const { public ImmutableList<Const> elements() { return elements; } + + @Override + public int hashCode() { + return elements.hashCode(); + } + + @Override + public boolean equals(Object obj) { + return obj instanceof ArrayInitValue && elements.equals(((ArrayInitValue) obj).elements); + } + + @Override + public String toString() { + return "{" + Joiner.on(", ").join(elements) + "}"; + } } } diff --git a/java/com/google/turbine/model/TurbineElementType.java b/java/com/google/turbine/model/TurbineElementType.java new file mode 100644 index 0000000..a68df3a --- /dev/null +++ b/java/com/google/turbine/model/TurbineElementType.java @@ -0,0 +1,32 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.model; + +/** Program locations where an annotation may appear. */ +public enum TurbineElementType { + ANNOTATION_TYPE, + CONSTRUCTOR, + FIELD, + LOCAL_VARIABLE, + METHOD, + MODULE, + PACKAGE, + PARAMETER, + TYPE, + TYPE_PARAMETER, + TYPE_USE +} diff --git a/java/com/google/turbine/options/TurbineOptions.java b/java/com/google/turbine/options/TurbineOptions.java index 04458e6..3bc9755 100644 --- a/java/com/google/turbine/options/TurbineOptions.java +++ b/java/com/google/turbine/options/TurbineOptions.java @@ -18,15 +18,15 @@ package com.google.turbine.options; import static com.google.common.base.Preconditions.checkNotNull; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import java.util.Optional; import javax.annotation.Nullable; /** Header compilation options. */ public class TurbineOptions { - private final String output; + private final Optional<String> output; private final ImmutableList<String> classPath; private final ImmutableSet<String> bootClassPath; private final Optional<String> release; @@ -41,15 +41,16 @@ public class TurbineOptions { private final Optional<String> injectingRuleKind; private final ImmutableList<String> depsArtifacts; private final boolean javacFallback; + private final boolean help; private final ImmutableList<String> javacOpts; private final boolean shouldReduceClassPath; private TurbineOptions( - String output, + @Nullable String output, ImmutableList<String> classPath, ImmutableSet<String> bootClassPath, - String release, - String system, + @Nullable String release, + @Nullable String system, ImmutableList<String> sources, ImmutableList<String> processorPath, ImmutableSet<String> processors, @@ -60,23 +61,25 @@ public class TurbineOptions { @Nullable String injectingRuleKind, ImmutableList<String> depsArtifacts, boolean javacFallback, + boolean help, ImmutableList<String> javacOpts, boolean shouldReduceClassPath) { - this.output = checkNotNull(output, "output must not be null"); + this.output = Optional.ofNullable(output); this.classPath = checkNotNull(classPath, "classPath must not be null"); this.bootClassPath = checkNotNull(bootClassPath, "bootClassPath must not be null"); - this.release = Optional.fromNullable(release); - this.system = Optional.fromNullable(system); + this.release = Optional.ofNullable(release); + this.system = Optional.ofNullable(system); this.sources = checkNotNull(sources, "sources must not be null"); this.processorPath = checkNotNull(processorPath, "processorPath must not be null"); this.processors = checkNotNull(processors, "processors must not be null"); this.sourceJars = checkNotNull(sourceJars, "sourceJars must not be null"); - this.outputDeps = Optional.fromNullable(outputDeps); + this.outputDeps = Optional.ofNullable(outputDeps); this.directJars = checkNotNull(directJars, "directJars must not be null"); - this.targetLabel = Optional.fromNullable(targetLabel); - this.injectingRuleKind = Optional.fromNullable(injectingRuleKind); + this.targetLabel = Optional.ofNullable(targetLabel); + this.injectingRuleKind = Optional.ofNullable(injectingRuleKind); this.depsArtifacts = checkNotNull(depsArtifacts, "depsArtifacts must not be null"); this.javacFallback = javacFallback; + this.help = help; this.javacOpts = checkNotNull(javacOpts, "javacOpts must not be null"); this.shouldReduceClassPath = shouldReduceClassPath; } @@ -107,10 +110,22 @@ public class TurbineOptions { } /** The output jar. */ - public String outputFile() { + @Nullable + public Optional<String> output() { return output; } + /** + * The output jar. + * + * @deprecated use {@link #output} instead. + */ + @Deprecated + @Nullable + public String outputFile() { + return output.orElse(null); + } + /** Paths to annotation processor artifacts. */ public ImmutableList<String> processorPath() { return processorPath; @@ -160,6 +175,11 @@ public class TurbineOptions { return javacFallback; } + /** Print usage information. */ + public boolean help() { + return help; + } + /** Additional Java compiler flags. */ public ImmutableList<String> javacOpts() { return javacOpts; @@ -192,6 +212,7 @@ public class TurbineOptions { @Nullable private String injectingRuleKind; private final ImmutableList.Builder<String> depsArtifacts = ImmutableList.builder(); private boolean javacFallback = true; + private boolean help = false; private final ImmutableList.Builder<String> javacOpts = ImmutableList.builder(); private boolean shouldReduceClassPath = true; @@ -212,6 +233,7 @@ public class TurbineOptions { injectingRuleKind, depsArtifacts.build(), javacFallback, + help, javacOpts.build(), shouldReduceClassPath); } @@ -291,6 +313,11 @@ public class TurbineOptions { return this; } + public Builder setHelp(boolean help) { + this.help = help; + return this; + } + public Builder addAllJavacOpts(Iterable<String> javacOpts) { this.javacOpts.addAll(javacOpts); return this; diff --git a/java/com/google/turbine/options/TurbineOptionsParser.java b/java/com/google/turbine/options/TurbineOptionsParser.java index f8fd8c3..2976285 100644 --- a/java/com/google/turbine/options/TurbineOptionsParser.java +++ b/java/com/google/turbine/options/TurbineOptionsParser.java @@ -116,6 +116,9 @@ public class TurbineOptionsParser { case "--nojavac_fallback": builder.setJavacFallback(false); break; + case "--help": + builder.setHelp(true); + break; default: throw new IllegalArgumentException("unknown option: " + next); } diff --git a/java/com/google/turbine/parse/ConstExpressionParser.java b/java/com/google/turbine/parse/ConstExpressionParser.java index 7b0f8c5..c6cb284 100644 --- a/java/com/google/turbine/parse/ConstExpressionParser.java +++ b/java/com/google/turbine/parse/ConstExpressionParser.java @@ -18,7 +18,6 @@ package com.google.turbine.parse; import static com.google.common.collect.Iterables.getOnlyElement; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.errorprone.annotations.CheckReturnValue; import com.google.turbine.diag.TurbineError; @@ -29,7 +28,9 @@ import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.ClassLiteral; import com.google.turbine.tree.Tree.ClassTy; import com.google.turbine.tree.Tree.Expression; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.TurbineOperatorKind; +import java.util.Optional; import javax.annotation.Nullable; /** A parser for compile-time constant expressions. */ @@ -230,11 +231,10 @@ public class ConstExpressionParser { } } - private static ClassTy asClassTy(int pos, ImmutableList<String> names) { + private static ClassTy asClassTy(int pos, ImmutableList<Tree.Ident> names) { ClassTy cty = null; - for (String bit : names) { - cty = - new ClassTy(pos, Optional.fromNullable(cty), bit, ImmutableList.of(), ImmutableList.of()); + for (Tree.Ident bit : names) { + cty = new ClassTy(pos, Optional.ofNullable(cty), bit, ImmutableList.of(), ImmutableList.of()); } return cty; } @@ -278,7 +278,7 @@ public class ConstExpressionParser { /** Finish hex, decimal, octal, and binary integer literals (see JLS 3.10.1). */ private Tree.Expression finishLiteral(TurbineConstantTypeKind kind, boolean negate) { - String text = lexer.stringValue(); + String text = ident().value(); Const.Value value; switch (kind) { case INT: @@ -422,8 +422,8 @@ public class ConstExpressionParser { @Nullable private Tree.Expression qualIdent() { int pos = position; - ImmutableList.Builder<String> bits = ImmutableList.builder(); - bits.add(lexer.stringValue()); + ImmutableList.Builder<Ident> bits = ImmutableList.builder(); + bits.add(ident()); eat(); if (token == Token.LBRACK) { return finishClassLiteral(pos, asClassTy(pos, bits.build())); @@ -432,7 +432,7 @@ public class ConstExpressionParser { eat(); switch (token) { case IDENT: - bits.add(lexer.stringValue()); + bits.add(ident()); break; case CLASS: // TODO(cushon): only allow in annotations? @@ -446,6 +446,10 @@ public class ConstExpressionParser { return new Tree.ConstVarName(pos, bits.build()); } + private Ident ident() { + return new Ident(lexer.position(), lexer.stringValue()); + } + private Expression finishClassLiteral(int pos, Tree.Type type) { while (token == Token.LBRACK) { eat(); @@ -518,12 +522,15 @@ public class ConstExpressionParser { if (!(term1 instanceof Tree.ConstVarName)) { return null; } - ImmutableList<String> names = ((Tree.ConstVarName) term1).name(); + ImmutableList<Ident> names = ((Tree.ConstVarName) term1).name(); if (names.size() > 1) { return null; } - String name = getOnlyElement(names); + Ident name = getOnlyElement(names); Tree.Expression rhs = expression(op.prec()); + if (rhs == null) { + return null; + } return new Tree.Assign(term1.position(), name, rhs); } @@ -560,7 +567,7 @@ public class ConstExpressionParser { throw new AssertionError(); } eat(); - ImmutableList<String> name = ((Tree.ConstVarName) qualIdent()).name(); + ImmutableList<Ident> name = ((Tree.ConstVarName) qualIdent()).name(); ImmutableList.Builder<Tree.Expression> args = ImmutableList.builder(); if (token == Token.LPAREN) { eat(); diff --git a/java/com/google/turbine/parse/Parser.java b/java/com/google/turbine/parse/Parser.java index 3858619..ba76659 100644 --- a/java/com/google/turbine/parse/Parser.java +++ b/java/com/google/turbine/parse/Parser.java @@ -17,16 +17,12 @@ package com.google.turbine.parse; import static com.google.turbine.parse.Token.COMMA; -import static com.google.turbine.parse.Token.IDENT; import static com.google.turbine.parse.Token.INTERFACE; import static com.google.turbine.parse.Token.LPAREN; import static com.google.turbine.parse.Token.RPAREN; -import static com.google.turbine.parse.Token.STATIC; import static com.google.turbine.tree.TurbineModifier.PROTECTED; import static com.google.turbine.tree.TurbineModifier.PUBLIC; -import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.errorprone.annotations.CheckReturnValue; @@ -41,6 +37,7 @@ import com.google.turbine.tree.Tree.ArrTy; import com.google.turbine.tree.Tree.ClassTy; import com.google.turbine.tree.Tree.CompUnit; import com.google.turbine.tree.Tree.Expression; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.ImportDecl; import com.google.turbine.tree.Tree.Kind; import com.google.turbine.tree.Tree.MethDecl; @@ -63,6 +60,7 @@ import java.util.ArrayDeque; import java.util.Deque; import java.util.EnumSet; import java.util.List; +import java.util.Optional; import javax.annotation.Nullable; /** @@ -95,8 +93,8 @@ public class Parser { // TODO(cushon): consider enforcing package, import, and declaration order // and make it bug-compatible with javac: // http://mail.openjdk.java.net/pipermail/compiler-dev/2013-August/006968.html - Optional<PkgDecl> pkg = Optional.absent(); - Optional<ModDecl> mod = Optional.absent(); + Optional<PkgDecl> pkg = Optional.empty(); + Optional<ModDecl> mod = Optional.empty(); EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); ImmutableList.Builder<ImportDecl> imports = ImmutableList.builder(); ImmutableList.Builder<TyDecl> decls = ImmutableList.builder(); @@ -184,14 +182,15 @@ public class Parser { continue; case IDENT: { - String ident = lexer.stringValue(); - if (access.isEmpty() && (ident.equals("module") || ident.equals("open"))) { + Ident ident = ident(); + if (access.isEmpty() + && (ident.value().equals("module") || ident.value().equals("open"))) { boolean open = false; - if (ident.equals("open")) { + if (ident.value().equals("open")) { ident = eatIdent(); open = true; } - if (!ident.equals("module")) { + if (!ident.value().equals("module")) { throw error(token); } next(); @@ -215,7 +214,7 @@ public class Parser { private TyDecl interfaceDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { eat(Token.INTERFACE); int pos = position; - String name = eatIdent(); + Ident name = eatIdent(); ImmutableList<TyParam> typarams; if (token == Token.LT) { typarams = typarams(); @@ -238,7 +237,7 @@ public class Parser { annos, name, typarams, - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), interfaces.build(), members, TurbineTyKind.INTERFACE); @@ -247,7 +246,7 @@ public class Parser { private TyDecl annotationDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { eat(Token.INTERFACE); int pos = position; - String name = eatIdent(); + Ident name = eatIdent(); eat(Token.LBRACE); ImmutableList<Tree> members = classMembers(); eat(Token.RBRACE); @@ -257,7 +256,7 @@ public class Parser { annos, name, ImmutableList.<TyParam>of(), - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), ImmutableList.<ClassTy>of(), members, TurbineTyKind.ANNOTATION); @@ -266,7 +265,7 @@ public class Parser { private TyDecl enumDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { eat(Token.ENUM); int pos = position; - String name = eatIdent(); + Ident name = eatIdent(); ImmutableList.Builder<ClassTy> interfaces = ImmutableList.builder(); if (token == Token.IMPLEMENTS) { next(); @@ -284,18 +283,18 @@ public class Parser { annos, name, ImmutableList.<TyParam>of(), - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), interfaces.build(), members, TurbineTyKind.ENUM); } private String moduleName() { - return Joiner.on('.').join(qualIdent()); + return flatname('.', qualIdent()); } private String packageName() { - return Joiner.on('/').join(qualIdent()); + return flatname('/', qualIdent()); } private ModDecl moduleDeclaration(boolean open, ImmutableList<Anno> annos) { @@ -340,6 +339,19 @@ public class Parser { return new ModDecl(pos, annos, open, moduleName, directives.build()); } + private String flatname(char join, ImmutableList<Ident> idents) { + StringBuilder sb = new StringBuilder(); + boolean first = true; + for (Ident ident : idents) { + if (!first) { + sb.append(join); + } + sb.append(ident.value()); + first = false; + } + return sb.toString(); + } + private ModRequires moduleRequires() { int pos = position; EnumSet<TurbineModifier> access = EnumSet.noneOf(TurbineModifier.class); @@ -356,6 +368,7 @@ public class Parser { } break; } + String moduleName = moduleName(); eat(Token.SEMI); return new ModRequires(pos, ImmutableSet.copyOf(access), moduleName); @@ -363,13 +376,14 @@ public class Parser { private ModExports moduleExports() { int pos = position; + String packageName = packageName(); ImmutableList.Builder<String> moduleNames = ImmutableList.builder(); if (lexer.stringValue().equals("to")) { next(); do { - String moduleName = moduleName(); - moduleNames.add(moduleName); + + moduleNames.add(moduleName()); } while (maybe(Token.COMMA)); } eat(Token.SEMI); @@ -378,11 +392,13 @@ public class Parser { private ModOpens moduleOpens() { int pos = position; + String packageName = packageName(); ImmutableList.Builder<String> moduleNames = ImmutableList.builder(); if (lexer.stringValue().equals("to")) { next(); do { + String moduleName = moduleName(); moduleNames.add(moduleName); } while (maybe(Token.COMMA)); @@ -393,20 +409,20 @@ public class Parser { private ModUses moduleUses() { int pos = position; - ImmutableList<String> uses = qualIdent(); + ImmutableList<Ident> uses = qualIdent(); eat(Token.SEMI); return new ModUses(pos, uses); } private ModProvides moduleProvides() { int pos = position; - ImmutableList<String> typeName = qualIdent(); - if (!eatIdent().equals("with")) { + ImmutableList<Ident> typeName = qualIdent(); + if (!eatIdent().value().equals("with")) { throw error(token); } - ImmutableList.Builder<ImmutableList<String>> implNames = ImmutableList.builder(); + ImmutableList.Builder<ImmutableList<Ident>> implNames = ImmutableList.builder(); do { - ImmutableList<String> implName = qualIdent(); + ImmutableList<Ident> implName = qualIdent(); implNames.add(implName); } while (maybe(Token.COMMA)); eat(Token.SEMI); @@ -420,7 +436,7 @@ public class Parser { TurbineModifier.ACC_ENUM, TurbineModifier.FINAL); - private ImmutableList<Tree> enumMembers(String enumName) { + private ImmutableList<Tree> enumMembers(Ident enumName) { ImmutableList.Builder<Tree> result = ImmutableList.builder(); ImmutableList.Builder<Anno> annos = ImmutableList.builder(); OUTER: @@ -428,7 +444,7 @@ public class Parser { switch (token) { case IDENT: { - String name = eatIdent(); + Ident name = eatIdent(); if (token == Token.LPAREN) { dropParens(); } @@ -444,12 +460,12 @@ public class Parser { annos.build(), new ClassTy( position, - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), enumName, ImmutableList.<Type>of(), ImmutableList.of()), name, - Optional.<Expression>absent())); + Optional.<Expression>empty())); annos = ImmutableList.builder(); break; } @@ -474,7 +490,7 @@ public class Parser { private TyDecl classDeclaration(EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { eat(Token.CLASS); int pos = position; - String name = eatIdent(); + Ident name = eatIdent(); ImmutableList<TyParam> tyParams = ImmutableList.of(); if (token == Token.LT) { tyParams = typarams(); @@ -500,7 +516,7 @@ public class Parser { annos, name, tyParams, - Optional.fromNullable(xtnds), + Optional.ofNullable(xtnds), interfaces.build(), members, TurbineTyKind.CLASS); @@ -624,7 +640,7 @@ public class Parser { EnumSet<TurbineModifier> access, ImmutableList<Anno> annos) { ImmutableList<TyParam> typaram = ImmutableList.of(); Type result; - String name; + Ident name; if (token == Token.LT) { typaram = typarams(); @@ -660,7 +676,7 @@ public class Parser { case IDENT: { int pos = position; - String ident = eatIdent(); + Ident ident = eatIdent(); switch (token) { case LPAREN: { @@ -672,7 +688,7 @@ public class Parser { result = new ClassTy( position, - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), ident, ImmutableList.<Type>of(), ImmutableList.of()); @@ -686,7 +702,7 @@ public class Parser { result = new ClassTy( position, - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), ident, ImmutableList.<Type>of(), ImmutableList.of()); @@ -697,7 +713,7 @@ public class Parser { { result = new ClassTy( - position, Optional.<ClassTy>absent(), ident, tyargs(), ImmutableList.of()); + position, Optional.<ClassTy>empty(), ident, tyargs(), ImmutableList.of()); result = maybeDims(maybeAnnos(), result); break; } @@ -705,7 +721,7 @@ public class Parser { result = new ClassTy( position, - Optional.<ClassTy>absent(), + Optional.<ClassTy>empty(), ident, ImmutableList.<Type>of(), ImmutableList.of()); @@ -764,12 +780,13 @@ public class Parser { ImmutableList<Anno> annos, ImmutableList<TyParam> typaram, Type result, - String name) { + Ident name) { switch (token) { case ASSIGN: - case SEMI: - case LBRACK: + case AT: case COMMA: + case LBRACK: + case SEMI: { if (!typaram.isEmpty()) { throw error(ErrorKind.UNEXPECTED_TYPE_PARAMETER, typaram); @@ -788,7 +805,7 @@ public class Parser { EnumSet<TurbineModifier> access, ImmutableList<Anno> annos, Type baseTy, - String name) { + Ident name) { ImmutableList.Builder<Tree> result = ImmutableList.builder(); VariableInitializerParser initializerParser = new VariableInitializerParser(token, lexer); List<List<SavedToken>> bits = initializerParser.parseInitializers(); @@ -810,7 +827,7 @@ public class Parser { if (init != null && init.kind() == Tree.Kind.ARRAY_INIT) { init = null; } - result.add(new VarDecl(pos, access, annos, ty, name, Optional.fromNullable(init))); + result.add(new VarDecl(pos, access, annos, ty, name, Optional.ofNullable(init))); } eat(Token.SEMI); return result.build(); @@ -822,7 +839,7 @@ public class Parser { ImmutableList<Anno> annos, ImmutableList<TyParam> typaram, Type result, - String name) { + Ident name) { eat(Token.LPAREN); ImmutableList.Builder<VarDecl> formals = ImmutableList.builder(); formalParams(formals, access); @@ -863,18 +880,18 @@ public class Parser { throw error(token); } if (result == null) { - name = CTOR_NAME; + name = new Ident(position, CTOR_NAME); } return new MethDecl( pos, access, annos, typaram, - Optional.<Tree>fromNullable(result), + Optional.<Tree>ofNullable(result), name, formals.build(), exceptions.build(), - Optional.fromNullable(defaultValue)); + Optional.ofNullable(defaultValue)); } /** @@ -948,23 +965,24 @@ public class Parser { } // the parameter name is `this` for receiver parameters, and a qualified this expression // for inner classes - String name = identOrThis(); + Ident name = identOrThis(); while (token == Token.DOT) { eat(Token.DOT); // Overwrite everything up to the terminal 'this' for inner classes; we don't need it name = identOrThis(); } ty = extraDims(ty); - return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>absent()); + return new VarDecl(position, access, annos.build(), ty, name, Optional.<Expression>empty()); } - private String identOrThis() { + private Ident identOrThis() { switch (token) { case IDENT: return eatIdent(); case THIS: + int position = lexer.position(); eat(Token.THIS); - return "this"; + return new Ident(position, "this"); default: throw error(token); } @@ -1016,7 +1034,7 @@ public class Parser { OUTER: while (true) { ImmutableList<Anno> annotations = maybeAnnos(); - String name = eatIdent(); + Ident name = eatIdent(); ImmutableList<Tree> bounds = ImmutableList.of(); if (token == Token.EXTENDS) { next(); @@ -1059,12 +1077,12 @@ public class Parser { if (typeAnnos == null) { typeAnnos = maybeAnnos(); } - String name = eatIdent(); + Ident name = eatIdent(); ImmutableList<Type> tyargs = ImmutableList.of(); if (token == Token.LT) { tyargs = tyargs(); } - ty = new ClassTy(pos, Optional.fromNullable(ty), name, tyargs, typeAnnos); + ty = new ClassTy(pos, Optional.ofNullable(ty), name, tyargs, typeAnnos); typeAnnos = null; } while (maybe(Token.DOT)); return ty; @@ -1085,25 +1103,25 @@ public class Parser { next(); Type upper = referenceType(maybeAnnos()); acc.add( - new WildTy(position, typeAnnos, Optional.of(upper), Optional.<Type>absent())); + new WildTy(position, typeAnnos, Optional.of(upper), Optional.<Type>empty())); break; case SUPER: next(); Type lower = referenceType(maybeAnnos()); acc.add( - new WildTy(position, typeAnnos, Optional.<Type>absent(), Optional.of(lower))); + new WildTy(position, typeAnnos, Optional.<Type>empty(), Optional.of(lower))); break; case COMMA: acc.add( new WildTy( - position, typeAnnos, Optional.<Type>absent(), Optional.<Type>absent())); + position, typeAnnos, Optional.<Type>empty(), Optional.<Type>empty())); continue OUTER; case GT: case GTGT: case GTGTGT: acc.add( new WildTy( - position, typeAnnos, Optional.<Type>absent(), Optional.<Type>absent())); + position, typeAnnos, Optional.<Type>empty(), Optional.<Type>empty())); break OUTER; default: throw error(token); @@ -1257,7 +1275,7 @@ public class Parser { boolean stat = maybe(Token.STATIC); int pos = position; - ImmutableList.Builder<String> type = ImmutableList.builder(); + ImmutableList.Builder<Ident> type = ImmutableList.builder(); type.add(eatIdent()); boolean wild = false; OUTER: @@ -1284,8 +1302,8 @@ public class Parser { return result; } - private ImmutableList<String> qualIdent() { - ImmutableList.Builder<String> name = ImmutableList.builder(); + private ImmutableList<Ident> qualIdent() { + ImmutableList.Builder<Ident> name = ImmutableList.builder(); name.add(eatIdent()); while (maybe(Token.DOT)) { name.add(eatIdent()); @@ -1295,7 +1313,7 @@ public class Parser { private Anno annotation() { int pos = position; - ImmutableList<String> name = qualIdent(); + ImmutableList<Ident> name = qualIdent(); ImmutableList.Builder<Expression> args = ImmutableList.builder(); if (token == Token.LPAREN) { @@ -1318,10 +1336,16 @@ public class Parser { return new Anno(pos, name, args.build()); } - private String eatIdent() { + private Ident ident() { + int position = lexer.position(); String value = lexer.stringValue(); + return new Ident(position, value); + } + + private Ident eatIdent() { + Ident ident = ident(); eat(Token.IDENT); - return value; + return ident; } private void eat(Token kind) { diff --git a/java/com/google/turbine/parse/StreamLexer.java b/java/com/google/turbine/parse/StreamLexer.java index 35fac45..74b0ce8 100644 --- a/java/com/google/turbine/parse/StreamLexer.java +++ b/java/com/google/turbine/parse/StreamLexer.java @@ -339,19 +339,23 @@ public class StreamLexer implements Lexer { { eat(); char value; - if (ch == '\\') { - eat(); - value = escape(); - } else { - value = ch; - eat(); + switch (ch) { + case '\\': + eat(); + value = escape(); + break; + case '\'': + throw error(ErrorKind.EMPTY_CHARACTER_LITERAL); + default: + value = ch; + eat(); } if (ch == '\'') { saveValue(String.valueOf(value)); eat(); return Token.CHAR_LITERAL; } - throw error(ErrorKind.UNEXPECTED_INPUT, ch); + throw error(ErrorKind.UNTERMINATED_CHARACTER_LITERAL); } case '"': @@ -370,6 +374,8 @@ public class StreamLexer implements Lexer { saveValue(sb.toString()); eat(); return Token.STRING_LITERAL; + case '\n': + throw error(ErrorKind.UNTERMINATED_STRING); case ASCII_SUB: if (reader.done()) { return Token.EOF; diff --git a/java/com/google/turbine/tree/Pretty.java b/java/com/google/turbine/tree/Pretty.java index 978be8d..2b9374e 100644 --- a/java/com/google/turbine/tree/Pretty.java +++ b/java/com/google/turbine/tree/Pretty.java @@ -22,6 +22,7 @@ import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.turbine.tree.Tree.Anno; import com.google.turbine.tree.Tree.ClassLiteral; +import com.google.turbine.tree.Tree.Ident; import com.google.turbine.tree.Tree.ModDecl; import com.google.turbine.tree.Tree.ModDirective; import com.google.turbine.tree.Tree.ModExports; @@ -80,6 +81,12 @@ public class Pretty implements Tree.Visitor<Void, Void> { } @Override + public Void visitIdent(Ident ident, Void input) { + sb.append(ident.value()); + return null; + } + + @Override public Void visitWildTy(Tree.WildTy wildTy, Void input) { printAnnos(wildTy.annos()); append('?'); @@ -124,7 +131,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { append('.'); } printAnnos(classTy.annos()); - append(classTy.name()); + append(classTy.name().value()); if (!classTy.tyargs().isEmpty()) { append('<'); boolean first = true; @@ -203,7 +210,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { @Override public Void visitAssign(Tree.Assign assign, Void input) { - append(assign.name()).append(" = "); + append(assign.name().value()).append(" = "); assign.expr().accept(this, null); return null; } @@ -280,7 +287,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { printAnnos(varDecl.annos()); printModifiers(varDecl.mods()); varDecl.ty().accept(this, null); - append(' ').append(varDecl.name()); + append(' ').append(varDecl.name().value()); if (varDecl.init().isPresent()) { append(" = "); varDecl.init().get().accept(this, null); @@ -318,7 +325,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { methDecl.ret().get().accept(this, null); append(' '); } - append(methDecl.name()); + append(methDecl.name().value()); append('('); boolean first = true; for (Tree.VarDecl param : methDecl.params()) { @@ -393,7 +400,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { append("@interface"); break; } - append(' ').append(tyDecl.name()); + append(' ').append(tyDecl.name().value()); if (!tyDecl.typarams().isEmpty()) { append('<'); boolean first = true; @@ -431,7 +438,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { if (t instanceof Tree.VarDecl) { Tree.VarDecl decl = (Tree.VarDecl) t; if (decl.mods().contains(TurbineModifier.ACC_ENUM)) { - append(decl.name()).append(',').append('\n'); + append(decl.name().value()).append(',').append('\n'); continue; } } @@ -503,7 +510,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { @Override public Void visitTyParam(Tree.TyParam tyParam, Void input) { printAnnos(tyParam.annos()); - append(tyParam.name()); + append(tyParam.name().value()); if (!tyParam.bounds().isEmpty()) { append(" extends "); boolean first = true; @@ -619,7 +626,7 @@ public class Pretty implements Tree.Visitor<Void, Void> { append(" with").append('\n'); indent += 2; boolean first = true; - for (ImmutableList<String> implName : modProvides.implNames()) { + for (ImmutableList<Ident> implName : modProvides.implNames()) { if (!first) { append(',').append('\n'); } diff --git a/java/com/google/turbine/tree/Tree.java b/java/com/google/turbine/tree/Tree.java index a84c776..a20b106 100644 --- a/java/com/google/turbine/tree/Tree.java +++ b/java/com/google/turbine/tree/Tree.java @@ -16,13 +16,16 @@ package com.google.turbine.tree; -import com.google.common.base.Optional; +import static java.util.Objects.requireNonNull; + import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; +import com.google.errorprone.annotations.Immutable; import com.google.turbine.diag.SourceFile; import com.google.turbine.model.Const; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.model.TurbineTyKind; +import java.util.Optional; import java.util.Set; /** An AST node. */ @@ -49,6 +52,7 @@ public abstract class Tree { /** Tree kind. */ public enum Kind { + IDENT, WILD_TY, ARR_TY, PRIM_TY, @@ -80,6 +84,37 @@ public abstract class Tree { MOD_PROVIDES } + /** An identifier. */ + @Immutable + public static class Ident extends Tree { + + private final String value; + + public Ident(int position, String value) { + super(position); + this.value = value; + } + + @Override + public Kind kind() { + return Kind.IDENT; + } + + @Override + public <I, O> O accept(Visitor<I, O> visitor, I input) { + return visitor.visitIdent(this, input); + } + + public String value() { + return value; + } + + @Override + public String toString() { + return value; + } + } + /** A type use. */ public abstract static class Type extends Tree { private final ImmutableList<Anno> annos; @@ -217,13 +252,13 @@ public abstract class Tree { /** A class, enum, interface, or annotation {@link Type}. */ public static class ClassTy extends Type { private final Optional<ClassTy> base; - private final String name; + private final Ident name; private final ImmutableList<Type> tyargs; public ClassTy( int position, Optional<ClassTy> base, - String name, + Ident name, ImmutableList<Type> tyargs, ImmutableList<Anno> annos) { super(position, annos); @@ -252,7 +287,7 @@ public abstract class Tree { } /** The simple name of the type. */ - public String name() { + public Ident name() { return name; } @@ -390,9 +425,9 @@ public abstract class Tree { /** A JLS 6.5.6.1 simple name that refers to a JSL 4.12.4 constant variable. */ public static class ConstVarName extends Expression { - private final ImmutableList<String> name; + private final ImmutableList<Ident> name; - public ConstVarName(int position, ImmutableList<String> name) { + public ConstVarName(int position, ImmutableList<Ident> name) { super(position); this.name = name; } @@ -407,7 +442,7 @@ public abstract class Tree { return visitor.visitConstVarName(this, input); } - public ImmutableList<String> name() { + public ImmutableList<Ident> name() { return name; } } @@ -439,13 +474,13 @@ public abstract class Tree { /** A JLS 15.26 assignment expression. */ public static class Assign extends Expression { - private final String name; + private final Ident name; private final Expression expr; - public Assign(int position, String name, Expression expr) { + public Assign(int position, Ident name, Expression expr) { super(position); - this.name = name; - this.expr = expr; + this.name = requireNonNull(name); + this.expr = requireNonNull(expr); } @Override @@ -458,7 +493,7 @@ public abstract class Tree { return visitor.visitAssign(this, input); } - public String name() { + public Ident name() { return name; } @@ -583,11 +618,11 @@ public abstract class Tree { /** A JLS 7.5 import declaration. */ public static class ImportDecl extends Tree { - private final ImmutableList<String> type; + private final ImmutableList<Ident> type; private final boolean stat; private final boolean wild; - public ImportDecl(int position, ImmutableList<String> type, boolean stat, boolean wild) { + public ImportDecl(int position, ImmutableList<Ident> type, boolean stat, boolean wild) { super(position); this.type = type; this.stat = stat; @@ -604,7 +639,7 @@ public abstract class Tree { return visitor.visitImportDecl(this, input); } - public ImmutableList<String> type() { + public ImmutableList<Ident> type() { return type; } @@ -624,7 +659,7 @@ public abstract class Tree { private final ImmutableSet<TurbineModifier> mods; private final ImmutableList<Anno> annos; private final Tree ty; - private final String name; + private final Ident name; private final Optional<Expression> init; public VarDecl( @@ -632,7 +667,7 @@ public abstract class Tree { Set<TurbineModifier> mods, ImmutableList<Anno> annos, Tree ty, - String name, + Ident name, Optional<Expression> init) { super(position); this.mods = ImmutableSet.copyOf(mods); @@ -664,7 +699,7 @@ public abstract class Tree { return ty; } - public String name() { + public Ident name() { return name; } @@ -679,7 +714,7 @@ public abstract class Tree { private final ImmutableList<Anno> annos; private final ImmutableList<TyParam> typarams; private final Optional<Tree> ret; - private final String name; + private final Ident name; private final ImmutableList<VarDecl> params; private final ImmutableList<ClassTy> exntys; private final Optional<Tree> defaultValue; @@ -690,7 +725,7 @@ public abstract class Tree { ImmutableList<Anno> annos, ImmutableList<TyParam> typarams, Optional<Tree> ret, - String name, + Ident name, ImmutableList<VarDecl> params, ImmutableList<ClassTy> exntys, Optional<Tree> defaultValue) { @@ -731,7 +766,7 @@ public abstract class Tree { return ret; } - public String name() { + public Ident name() { return name; } @@ -750,10 +785,10 @@ public abstract class Tree { /** A JLS 9.7 annotation. */ public static class Anno extends Tree { - private final ImmutableList<String> name; + private final ImmutableList<Ident> name; private final ImmutableList<Expression> args; - public Anno(int position, ImmutableList<String> name, ImmutableList<Expression> args) { + public Anno(int position, ImmutableList<Ident> name, ImmutableList<Expression> args) { super(position); this.name = name; this.args = args; @@ -769,7 +804,7 @@ public abstract class Tree { return visitor.visitAnno(this, input); } - public ImmutableList<String> name() { + public ImmutableList<Ident> name() { return name; } @@ -811,7 +846,7 @@ public abstract class Tree { public static class TyDecl extends Tree { private final ImmutableSet<TurbineModifier> mods; private final ImmutableList<Anno> annos; - private final String name; + private final Ident name; private final ImmutableList<TyParam> typarams; private final Optional<ClassTy> xtnds; private final ImmutableList<ClassTy> impls; @@ -822,7 +857,7 @@ public abstract class Tree { int position, Set<TurbineModifier> mods, ImmutableList<Anno> annos, - String name, + Ident name, ImmutableList<TyParam> typarams, Optional<ClassTy> xtnds, ImmutableList<ClassTy> impls, @@ -857,7 +892,7 @@ public abstract class Tree { return annos; } - public String name() { + public Ident name() { return name; } @@ -884,12 +919,12 @@ public abstract class Tree { /** A JLS 4.4. type variable declaration. */ public static class TyParam extends Tree { - private final String name; + private final Ident name; private final ImmutableList<Tree> bounds; private final ImmutableList<Anno> annos; public TyParam( - int position, String name, ImmutableList<Tree> bounds, ImmutableList<Anno> annos) { + int position, Ident name, ImmutableList<Tree> bounds, ImmutableList<Anno> annos) { super(position); this.name = name; this.bounds = bounds; @@ -906,7 +941,7 @@ public abstract class Tree { return visitor.visitTyParam(this, input); } - public String name() { + public Ident name() { return name; } @@ -921,10 +956,10 @@ public abstract class Tree { /** A JLS 7.4 package declaration. */ public static class PkgDecl extends Tree { - private final ImmutableList<String> name; + private final ImmutableList<Ident> name; private final ImmutableList<Anno> annos; - public PkgDecl(int position, ImmutableList<String> name, ImmutableList<Anno> annos) { + public PkgDecl(int position, ImmutableList<Ident> name, ImmutableList<Anno> annos) { super(position); this.name = name; this.annos = annos; @@ -940,7 +975,7 @@ public abstract class Tree { return visitor.visitPkgDecl(this, input); } - public ImmutableList<String> name() { + public ImmutableList<Ident> name() { return name; } @@ -1127,14 +1162,14 @@ public abstract class Tree { /** A JLS 7.7.3 module uses directive. */ public static class ModUses extends ModDirective { - private final ImmutableList<String> typeName; + private final ImmutableList<Ident> typeName; - public ModUses(int position, ImmutableList<String> typeName) { + public ModUses(int position, ImmutableList<Ident> typeName) { super(position); this.typeName = typeName; } - public ImmutableList<String> typeName() { + public ImmutableList<Ident> typeName() { return typeName; } @@ -1157,23 +1192,23 @@ public abstract class Tree { /** A JLS 7.7.4 module uses directive. */ public static class ModProvides extends ModDirective { - private final ImmutableList<String> typeName; - private final ImmutableList<ImmutableList<String>> implNames; + private final ImmutableList<Ident> typeName; + private final ImmutableList<ImmutableList<Ident>> implNames; public ModProvides( int position, - ImmutableList<String> typeName, - ImmutableList<ImmutableList<String>> implNames) { + ImmutableList<Ident> typeName, + ImmutableList<ImmutableList<Ident>> implNames) { super(position); this.typeName = typeName; this.implNames = implNames; } - public ImmutableList<String> typeName() { + public ImmutableList<Ident> typeName() { return typeName; } - public ImmutableList<ImmutableList<String>> implNames() { + public ImmutableList<ImmutableList<Ident>> implNames() { return implNames; } @@ -1195,6 +1230,8 @@ public abstract class Tree { /** A visitor for {@link Tree}s. */ public interface Visitor<I, O> { + O visitIdent(Ident ident, I input); + O visitWildTy(WildTy visitor, I input); O visitArrTy(ArrTy arrTy, I input); diff --git a/java/com/google/turbine/type/AnnoInfo.java b/java/com/google/turbine/type/AnnoInfo.java index c564f48..9c907aa 100644 --- a/java/com/google/turbine/type/AnnoInfo.java +++ b/java/com/google/turbine/type/AnnoInfo.java @@ -16,8 +16,6 @@ package com.google.turbine.type; -import static java.util.Objects.requireNonNull; - import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.sym.ClassSymbol; @@ -26,6 +24,7 @@ import com.google.turbine.model.Const; import com.google.turbine.tree.Tree; import com.google.turbine.tree.Tree.Anno; import com.google.turbine.tree.Tree.Expression; +import java.util.Objects; /** An annotation use. */ public class AnnoInfo { @@ -37,7 +36,7 @@ public class AnnoInfo { public AnnoInfo( SourceFile source, ClassSymbol sym, Anno tree, ImmutableMap<String, Const> values) { this.source = source; - this.sym = requireNonNull(sym); + this.sym = sym; this.tree = tree; this.values = values; } @@ -70,4 +69,18 @@ public class AnnoInfo { public AnnoInfo withValues(ImmutableMap<String, Const> values) { return new AnnoInfo(source, sym, tree, values); } + + @Override + public int hashCode() { + return Objects.hash(sym, values); + } + + @Override + public boolean equals(Object obj) { + if (!(obj instanceof AnnoInfo)) { + return false; + } + AnnoInfo that = (AnnoInfo) obj; + return sym.equals(that.sym) && values.equals(that.values); + } } diff --git a/java/com/google/turbine/type/Type.java b/java/com/google/turbine/type/Type.java index 61a5bbe..3bf47d6 100644 --- a/java/com/google/turbine/type/Type.java +++ b/java/com/google/turbine/type/Type.java @@ -16,8 +16,8 @@ package com.google.turbine.type; +import com.google.auto.value.AutoValue; import com.google.common.base.Joiner; -import com.google.common.base.Preconditions; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.binder.sym.TyVarSymbol; @@ -44,7 +44,11 @@ public interface Type { /** A type variable type. */ TY_VAR, /** A wildcard type. */ - WILD_TY + WILD_TY, + /** An intersection type. */ + INTERSECTION_TY, + + ERROR_TY } /** The type kind. */ @@ -60,7 +64,8 @@ public interface Type { }; /** A class type. */ - class ClassTy implements Type { + @AutoValue + abstract class ClassTy implements Type { /** * The {@link ClassTy} for {@code java.lang.Object}. There's nothing special about this @@ -73,11 +78,10 @@ public interface Type { /** Returns a {@link ClassTy} with no type arguments for the given {@link ClassSymbol}. */ public static ClassTy asNonParametricClassTy(ClassSymbol i) { - return new ClassTy( - Arrays.asList(new SimpleClassTy(i, ImmutableList.of(), ImmutableList.of()))); + return create(Arrays.asList(SimpleClassTy.create(i, ImmutableList.of(), ImmutableList.of()))); } - public final ImmutableList<SimpleClassTy> classes; + public abstract ImmutableList<SimpleClassTy> classes(); /** * A class type. Qualified types are repesented as a list tuples, each of which contains a @@ -85,8 +89,8 @@ public interface Type { * * @param classes components of a qualified class type, possibly with type arguments. */ - public ClassTy(Iterable<SimpleClassTy> classes) { - this.classes = ImmutableList.copyOf(classes); + public static ClassTy create(Iterable<SimpleClassTy> classes) { + return new AutoValue_Type_ClassTy(ImmutableList.copyOf(classes)); } @Override @@ -96,23 +100,23 @@ public interface Type { /** The class symbol. */ public ClassSymbol sym() { - return classes.get(classes.size() - 1).sym; + return classes().get(classes().size() - 1).sym(); } @Override - public String toString() { + public final String toString() { StringBuilder sb = new StringBuilder(); boolean first = true; - for (SimpleClassTy c : classes) { + for (SimpleClassTy c : classes()) { if (!first) { sb.append('.'); - sb.append(c.sym.binaryName().substring(c.sym.binaryName().lastIndexOf('$') + 1)); + sb.append(c.sym().binaryName().substring(c.sym().binaryName().lastIndexOf('$') + 1)); } else { - sb.append(c.sym.binaryName()); + sb.append(c.sym().binaryName()); } - if (!c.targs.isEmpty()) { + if (!c.targs().isEmpty()) { sb.append('<'); - Joiner.on(',').appendTo(sb, c.targs); + Joiner.on(',').appendTo(sb, c.targs()); sb.append('>'); } first = false; @@ -121,53 +125,35 @@ public interface Type { } /** One element of a qualified {@link ClassTy}. */ - public static class SimpleClassTy { - - private final ClassSymbol sym; - private final ImmutableList<Type> targs; - private final ImmutableList<AnnoInfo> annos; + @AutoValue + public abstract static class SimpleClassTy { - public SimpleClassTy( + public static SimpleClassTy create( ClassSymbol sym, ImmutableList<Type> targs, ImmutableList<AnnoInfo> annos) { - Preconditions.checkNotNull(sym); - Preconditions.checkNotNull(targs); - this.sym = sym; - this.targs = targs; - this.annos = annos; + return new AutoValue_Type_ClassTy_SimpleClassTy(sym, targs, annos); } /** The class symbol of the element. */ - public ClassSymbol sym() { - return sym; - } + public abstract ClassSymbol sym(); /** The type arguments. */ - public ImmutableList<Type> targs() { - return targs; - } + public abstract ImmutableList<Type> targs(); /** The type annotations. */ - public ImmutableList<AnnoInfo> annos() { - return annos; - } + public abstract ImmutableList<AnnoInfo> annos(); } } /** An array type. */ - class ArrayTy implements Type { - - private final Type elem; - private final ImmutableList<AnnoInfo> annos; + @AutoValue + abstract class ArrayTy implements Type { - public ArrayTy(Type elem, ImmutableList<AnnoInfo> annos) { - this.elem = elem; - this.annos = annos; + public static ArrayTy create(Type elem, ImmutableList<AnnoInfo> annos) { + return new AutoValue_Type_ArrayTy(elem, annos); } /** The element type of the array. */ - public Type elementType() { - return elem; - } + public abstract Type elementType(); @Override public TyKind tyKind() { @@ -175,26 +161,19 @@ public interface Type { } /** The type annotations. */ - public ImmutableList<AnnoInfo> annos() { - return annos; - } + public abstract ImmutableList<AnnoInfo> annos(); } /** A type variable. */ - class TyVar implements Type { - - private final TyVarSymbol sym; - private final ImmutableList<AnnoInfo> annos; + @AutoValue + abstract class TyVar implements Type { - public TyVar(TyVarSymbol sym, ImmutableList<AnnoInfo> annos) { - this.sym = sym; - this.annos = annos; + public static TyVar create(TyVarSymbol sym, ImmutableList<AnnoInfo> annos) { + return new AutoValue_Type_TyVar(sym, annos); } /** The type variable's symbol. */ - public TyVarSymbol sym() { - return sym; - } + public abstract TyVarSymbol sym(); @Override public TyKind tyKind() { @@ -202,31 +181,24 @@ public interface Type { } @Override - public String toString() { - return sym.owner() + "#" + sym.name(); + public final String toString() { + return sym().owner() + "#" + sym().name(); } /** The type annotations. */ - public ImmutableList<AnnoInfo> annos() { - return annos; - } + public abstract ImmutableList<AnnoInfo> annos(); } /** A primitive type. */ - class PrimTy implements Type { + @AutoValue + abstract class PrimTy implements Type { - private final TurbineConstantTypeKind primtkind; - private final ImmutableList<AnnoInfo> annos; - - public PrimTy(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos) { - this.primtkind = tykind; - this.annos = annos; + public static PrimTy create(TurbineConstantTypeKind tykind, ImmutableList<AnnoInfo> annos) { + return new AutoValue_Type_PrimTy(tykind, annos); } /** The primtive type kind. */ - public TurbineConstantTypeKind primkind() { - return primtkind; - } + public abstract TurbineConstantTypeKind primkind(); @Override public TyKind tyKind() { @@ -234,9 +206,7 @@ public interface Type { } /** The type annotations. */ - public ImmutableList<AnnoInfo> annos() { - return annos; - } + public abstract ImmutableList<AnnoInfo> annos(); } /** A wildcard type, valid only inside (possibly nested) type arguments. */ @@ -262,26 +232,16 @@ public interface Type { } /** An upper-bounded wildcard type. */ - class WildUpperBoundedTy extends WildTy { + @AutoValue + abstract class WildUpperBoundedTy extends WildTy { - public final Type bound; - private final ImmutableList<AnnoInfo> annotations; - - public WildUpperBoundedTy(Type bound, ImmutableList<AnnoInfo> annotations) { - this.bound = bound; - this.annotations = annotations; + public static WildUpperBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) { + return new AutoValue_Type_WildUpperBoundedTy(annotations, bound); } /** The upper bound. */ @Override - public Type bound() { - return bound; - } - - @Override - public ImmutableList<AnnoInfo> annotations() { - return annotations; - } + public abstract Type bound(); @Override public BoundKind boundKind() { @@ -290,40 +250,29 @@ public interface Type { } /** An lower-bounded wildcard type. */ - class WildLowerBoundedTy extends WildTy { + @AutoValue + abstract class WildLowerBoundedTy extends WildTy { - public final Type bound; - private final ImmutableList<AnnoInfo> annotations; - - public WildLowerBoundedTy(Type bound, ImmutableList<AnnoInfo> annotations) { - this.bound = bound; - this.annotations = annotations; + public static WildLowerBoundedTy create(Type bound, ImmutableList<AnnoInfo> annotations) { + return new AutoValue_Type_WildLowerBoundedTy(annotations, bound); } /** The lower bound. */ @Override - public Type bound() { - return bound; - } + public abstract Type bound(); @Override public BoundKind boundKind() { return BoundKind.LOWER; } - - @Override - public ImmutableList<AnnoInfo> annotations() { - return annotations; - } } /** An unbounded wildcard type. */ - class WildUnboundedTy extends WildTy { - - private final ImmutableList<AnnoInfo> annotations; + @AutoValue + abstract class WildUnboundedTy extends WildTy { - public WildUnboundedTy(ImmutableList<AnnoInfo> annotations) { - this.annotations = annotations; + public static WildUnboundedTy create(ImmutableList<AnnoInfo> annotations) { + return new AutoValue_Type_WildUnboundedTy(annotations); } @Override @@ -335,10 +284,34 @@ public interface Type { public Type bound() { throw new IllegalStateException(); } + } + + /** An intersection type. */ + @AutoValue + abstract class IntersectionTy implements Type { + + public abstract ImmutableList<Type> bounds(); + + public static IntersectionTy create(ImmutableList<Type> bounds) { + return new AutoValue_Type_IntersectionTy(bounds); + } + + @Override + public TyKind tyKind() { + return TyKind.INTERSECTION_TY; + } + } + + /** An error type. */ + @AutoValue + abstract class ErrorTy implements Type { + public static ErrorTy create() { + return new AutoValue_Type_ErrorTy(); + } @Override - public ImmutableList<AnnoInfo> annotations() { - return annotations; + public TyKind tyKind() { + return TyKind.ERROR_TY; } } } diff --git a/java/com/google/turbine/types/Canonicalize.java b/java/com/google/turbine/types/Canonicalize.java index 98adf57..d3328b7 100644 --- a/java/com/google/turbine/types/Canonicalize.java +++ b/java/com/google/turbine/types/Canonicalize.java @@ -30,6 +30,7 @@ import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; import com.google.turbine.type.Type.ClassTy; import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.IntersectionTy; import com.google.turbine.type.Type.TyKind; import com.google.turbine.type.Type.TyVar; import com.google.turbine.type.Type.WildTy; @@ -66,46 +67,72 @@ public class Canonicalize { /** Canonicalizes the given type. */ public static Type canonicalize( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, Type type) { + SourceFile source, + int position, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol sym, + Type type) { + return new Canonicalize(source, position, env).canonicalize(sym, type); + } + + /** Canonicalize a qualified class type, excluding type arguments. */ + public static ClassTy canonicalizeClassTy( + SourceFile source, + int position, + Env<ClassSymbol, TypeBoundClass> env, + ClassSymbol owner, + ClassTy classTy) { + return new Canonicalize(source, position, env).canonicalizeClassTy(owner, classTy); + } + + private final SourceFile source; + private final int position; + private final Env<ClassSymbol, TypeBoundClass> env; + + public Canonicalize(SourceFile source, int position, Env<ClassSymbol, TypeBoundClass> env) { + this.source = source; + this.position = position; + this.env = env; + } + + private Type canonicalize(ClassSymbol base, Type type) { switch (type.tyKind()) { case PRIM_TY: case VOID_TY: case TY_VAR: return type; case WILD_TY: - return canonicalizeWildTy(source, env, base, (WildTy) type); + return canonicalizeWildTy(base, (WildTy) type); case ARRAY_TY: { Type.ArrayTy arrayTy = (Type.ArrayTy) type; - return new Type.ArrayTy( - canonicalize(source, env, base, arrayTy.elementType()), arrayTy.annos()); + return Type.ArrayTy.create(canonicalize(base, arrayTy.elementType()), arrayTy.annos()); } case CLASS_TY: - return canonicalizeClassTy(source, env, base, (ClassTy) type); + return canonicalizeClassTy(base, (ClassTy) type); + case INTERSECTION_TY: + return canonicalizeIntersectionTy(base, (IntersectionTy) type); default: throw new AssertionError(type.tyKind()); } } - /** Canonicalize a qualified class type, excluding type arguments. */ - private static ClassTy canon( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { - if (isRaw(env, ty)) { + private ClassTy canon(ClassSymbol base, ClassTy ty) { + if (isRaw(ty)) { return Erasure.eraseClassTy(ty); } // if the first name is a simple name resolved inside a nested class, add explicit qualifiers // for the enclosing declarations - Iterator<ClassTy.SimpleClassTy> it = ty.classes.iterator(); - Collection<ClassTy.SimpleClassTy> lexicalBase = - lexicalBase(source, env, ty.classes.get(0).sym(), base); + Iterator<ClassTy.SimpleClassTy> it = ty.classes().iterator(); + Collection<ClassTy.SimpleClassTy> lexicalBase = lexicalBase(ty.classes().get(0).sym(), base); ClassTy canon = !lexicalBase.isEmpty() - ? new ClassTy(lexicalBase) - : new ClassTy(Collections.singletonList(it.next())); + ? ClassTy.create(lexicalBase) + : ClassTy.create(Collections.singletonList(it.next())); // canonicalize each additional simple name that appeared in source while (it.hasNext()) { - canon = canonOne(source, env, canon, it.next()); + canon = canonOne(canon, it.next()); } return canon; } @@ -113,9 +140,9 @@ public class Canonicalize { /** * Qualified type names cannot be partially raw; if any elements are raw erase the entire type. */ - private static boolean isRaw(Env<ClassSymbol, TypeBoundClass> env, ClassTy ty) { - for (ClassTy.SimpleClassTy s : ty.classes.reverse()) { - TypeBoundClass info = env.get(s.sym()); + private boolean isRaw(ClassTy ty) { + for (ClassTy.SimpleClassTy s : ty.classes().reverse()) { + TypeBoundClass info = getInfo(s.sym()); if (s.targs().isEmpty() && !info.typeParameters().isEmpty()) { return true; } @@ -127,50 +154,42 @@ public class Canonicalize { } /** Given a base symbol to canonicalize, find any implicit enclosing instances. */ - private static Collection<ClassTy.SimpleClassTy> lexicalBase( - SourceFile source, - Env<ClassSymbol, TypeBoundClass> env, - ClassSymbol first, - ClassSymbol owner) { - if ((env.get(first).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + private Collection<ClassTy.SimpleClassTy> lexicalBase(ClassSymbol first, ClassSymbol owner) { + + if ((getInfo(first).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { return Collections.emptyList(); } - ClassSymbol canonOwner = env.get(first).owner(); + ClassSymbol canonOwner = getInfo(first).owner(); Deque<ClassTy.SimpleClassTy> result = new ArrayDeque<>(); while (canonOwner != null && owner != null) { - if (!isSubclass(env, owner, canonOwner)) { - owner = env.get(owner).owner(); + if (!isSubclass(owner, canonOwner)) { + owner = getInfo(owner).owner(); continue; } - result.addFirst(uninstantiated(env, owner)); - if ((env.get(owner).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + result.addFirst(uninstantiated(owner)); + if ((getInfo(owner).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { break; } - TypeBoundClass info = env.get(canonOwner); - if (info == null) { - throw TurbineError.format(source, ErrorKind.CLASS_FILE_NOT_FOUND, canonOwner); - } - canonOwner = info.owner(); + canonOwner = getInfo(canonOwner).owner(); } return result; } - private static ClassTy.SimpleClassTy uninstantiated( - Env<ClassSymbol, TypeBoundClass> env, ClassSymbol owner) { + private ClassTy.SimpleClassTy uninstantiated(ClassSymbol owner) { ImmutableList.Builder<Type> targs = ImmutableList.builder(); - for (TyVarSymbol p : env.get(owner).typeParameterTypes().keySet()) { - targs.add(new Type.TyVar(p, ImmutableList.of())); + for (TyVarSymbol p : getInfo(owner).typeParameterTypes().keySet()) { + targs.add(Type.TyVar.create(p, ImmutableList.of())); } - return new ClassTy.SimpleClassTy(owner, targs.build(), ImmutableList.of()); + return ClassTy.SimpleClassTy.create(owner, targs.build(), ImmutableList.of()); } // is s a subclass (not interface) of t? - static boolean isSubclass(Env<ClassSymbol, TypeBoundClass> env, ClassSymbol s, ClassSymbol t) { + private boolean isSubclass(ClassSymbol s, ClassSymbol t) { while (s != null) { if (s.equals(t)) { return true; } - s = env.get(s).superclass(); + s = getInfo(s).superclass(); } return false; } @@ -179,48 +198,44 @@ public class Canonicalize { * Adds a simple class type to an existing canonical base class type, and canonicalizes the * result. */ - private static ClassTy canonOne( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassTy base, SimpleClassTy ty) { + private ClassTy canonOne(ClassTy base, SimpleClassTy ty) { // if the class is static, it has a trivial canonical qualifier with no type arguments - if ((env.get(ty.sym()).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { - return new ClassTy(Collections.singletonList(ty)); + if ((getInfo(ty.sym()).access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) { + return ClassTy.create(ImmutableList.of(ty)); } ImmutableList.Builder<ClassTy.SimpleClassTy> simples = ImmutableList.builder(); - ClassSymbol owner = env.get(ty.sym()).owner(); + ClassSymbol owner = getInfo(ty.sym()).owner(); if (owner.equals(base.sym())) { // if the canonical prefix is the owner the next symbol in the qualified name, // the type is already in canonical form - simples.addAll(base.classes); + simples.addAll(base.classes()); simples.add(ty); - return new ClassTy(simples.build()); + return ClassTy.create(simples.build()); } // ... otherwise, find the supertype the class was inherited from // and instantiate it as a member of the current class ClassTy curr = base; Map<TyVarSymbol, Type> mapping = new LinkedHashMap<>(); while (curr != null) { - for (ClassTy.SimpleClassTy s : curr.classes) { - addInstantiation(env, mapping, s); + for (ClassTy.SimpleClassTy s : curr.classes()) { + addInstantiation(mapping, s); } if (curr.sym().equals(owner)) { - for (ClassTy.SimpleClassTy s : curr.classes) { - simples.add(instantiate(env, mapping, s.sym())); + for (ClassTy.SimpleClassTy s : curr.classes()) { + simples.add(instantiate(mapping, s.sym())); } break; } - TypeBoundClass info = env.get(curr.sym()); - curr = canon(source, env, info.owner(), info.superClassType()); + TypeBoundClass info = getInfo(curr.sym()); + curr = canon(info.owner(), (ClassTy) info.superClassType()); } simples.add(ty); - return new ClassTy(simples.build()); + return ClassTy.create(simples.build()); } /** Add the type arguments of a simple class type to a type mapping. */ - static void addInstantiation( - Env<ClassSymbol, TypeBoundClass> env, - Map<TyVarSymbol, Type> mapping, - ClassTy.SimpleClassTy simpleType) { - Collection<TyVarSymbol> symbols = env.get(simpleType.sym()).typeParameters().values(); + private void addInstantiation(Map<TyVarSymbol, Type> mapping, ClassTy.SimpleClassTy simpleType) { + Collection<TyVarSymbol> symbols = getInfo(simpleType.sym()).typeParameters().values(); if (simpleType.targs().isEmpty()) { // the type is raw for (TyVarSymbol sym : symbols) { @@ -241,14 +256,12 @@ public class Canonicalize { } /** Instantiate a simple class type for the given symbol, and with the given type mapping. */ - static ClassTy.SimpleClassTy instantiate( - Env<ClassSymbol, TypeBoundClass> env, - Map<TyVarSymbol, Type> mapping, - ClassSymbol classSymbol) { + private ClassTy.SimpleClassTy instantiate( + Map<TyVarSymbol, Type> mapping, ClassSymbol classSymbol) { List<Type> args = new ArrayList<>(); - for (TyVarSymbol sym : env.get(classSymbol).typeParameterTypes().keySet()) { + for (TyVarSymbol sym : getInfo(classSymbol).typeParameterTypes().keySet()) { if (!mapping.containsKey(sym)) { - args.add(new Type.TyVar(sym, ImmutableList.of())); + args.add(Type.TyVar.create(sym, ImmutableList.of())); continue; } Type arg = instantiate(mapping, mapping.get(sym)); @@ -259,11 +272,12 @@ public class Canonicalize { } args.add(arg); } - return new ClassTy.SimpleClassTy(classSymbol, ImmutableList.copyOf(args), ImmutableList.of()); + return ClassTy.SimpleClassTy.create( + classSymbol, ImmutableList.copyOf(args), ImmutableList.of()); } /** Instantiates a type argument using the given mapping. */ - private static Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) { + private Type instantiate(Map<TyVarSymbol, Type> mapping, Type type) { if (type == null) { return null; } @@ -278,7 +292,7 @@ public class Canonicalize { case ARRAY_TY: ArrayTy arrayTy = (ArrayTy) type; Type elem = instantiate(mapping, arrayTy.elementType()); - return new ArrayTy(elem, arrayTy.annos()); + return ArrayTy.create(elem, arrayTy.annos()); case TY_VAR: TyVar tyVar = (TyVar) type; if (mapping.containsKey(tyVar.sym())) { @@ -290,29 +304,31 @@ public class Canonicalize { } } - private static Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) { + private Type instantiateWildTy(Map<TyVarSymbol, Type> mapping, WildTy type) { switch (type.boundKind()) { case NONE: return type; case UPPER: - return new Type.WildUpperBoundedTy(instantiate(mapping, type.bound()), type.annotations()); + return Type.WildUpperBoundedTy.create( + instantiate(mapping, type.bound()), type.annotations()); case LOWER: - return new Type.WildLowerBoundedTy(instantiate(mapping, type.bound()), type.annotations()); + return Type.WildLowerBoundedTy.create( + instantiate(mapping, type.bound()), type.annotations()); default: throw new AssertionError(type.boundKind()); } } - private static Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) { + private Type instantiateClassTy(Map<TyVarSymbol, Type> mapping, ClassTy type) { ImmutableList.Builder<SimpleClassTy> simples = ImmutableList.builder(); - for (SimpleClassTy simple : type.classes) { + for (SimpleClassTy simple : type.classes()) { ImmutableList.Builder<Type> args = ImmutableList.builder(); for (Type arg : simple.targs()) { args.add(instantiate(mapping, arg)); } - simples.add(new SimpleClassTy(simple.sym(), args.build(), simple.annos())); + simples.add(SimpleClassTy.create(simple.sym(), args.build(), simple.annos())); } - return new ClassTy(simples.build()); + return ClassTy.create(simples.build()); } /** @@ -320,51 +336,56 @@ public class Canonicalize { * reference, or else {@code null}. */ @Nullable - private static TyVarSymbol tyVarSym(Type type) { + private TyVarSymbol tyVarSym(Type type) { if (type.tyKind() == TyKind.TY_VAR) { return ((TyVar) type).sym(); } return null; } - public static ClassTy canonicalizeClassTy( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, ClassTy ty) { + private ClassTy canonicalizeClassTy(ClassSymbol base, ClassTy ty) { // canonicalize type arguments first ImmutableList.Builder<ClassTy.SimpleClassTy> args = ImmutableList.builder(); - for (ClassTy.SimpleClassTy s : ty.classes) { - args.add( - new ClassTy.SimpleClassTy( - s.sym(), canonicalize(source, s.targs(), base, env), s.annos())); + for (ClassTy.SimpleClassTy s : ty.classes()) { + args.add(SimpleClassTy.create(s.sym(), canonicalize(s.targs(), base), s.annos())); } - ty = new ClassTy(args.build()); - return canon(source, env, base, ty); + ty = ClassTy.create(args.build()); + return canon(base, ty); } - private static ImmutableList<Type> canonicalize( - SourceFile source, - ImmutableList<Type> targs, - ClassSymbol base, - Env<ClassSymbol, TypeBoundClass> env) { + private ImmutableList<Type> canonicalize(ImmutableList<Type> targs, ClassSymbol base) { ImmutableList.Builder<Type> result = ImmutableList.builder(); for (Type a : targs) { - result.add(canonicalize(source, env, base, a)); + result.add(canonicalize(base, a)); } return result.build(); } - private static Type canonicalizeWildTy( - SourceFile source, Env<ClassSymbol, TypeBoundClass> env, ClassSymbol base, WildTy type) { + private Type canonicalizeWildTy(ClassSymbol base, WildTy type) { switch (type.boundKind()) { case NONE: return type; case LOWER: - return new Type.WildLowerBoundedTy( - canonicalize(source, env, base, type.bound()), type.annotations()); + return Type.WildLowerBoundedTy.create(canonicalize(base, type.bound()), type.annotations()); case UPPER: - return new Type.WildUpperBoundedTy( - canonicalize(source, env, base, type.bound()), type.annotations()); - default: - throw new AssertionError(type.boundKind()); + return Type.WildUpperBoundedTy.create(canonicalize(base, type.bound()), type.annotations()); + } + throw new AssertionError(type.boundKind()); + } + + private Type canonicalizeIntersectionTy(ClassSymbol base, IntersectionTy type) { + ImmutableList.Builder<Type> bounds = ImmutableList.builder(); + for (Type bound : type.bounds()) { + bounds.add(canonicalize(base, bound)); + } + return IntersectionTy.create(bounds.build()); + } + + private TypeBoundClass getInfo(ClassSymbol canonOwner) { + TypeBoundClass info = env.get(canonOwner); + if (info == null) { + throw TurbineError.format(source, position, ErrorKind.CLASS_FILE_NOT_FOUND, canonOwner); } + return info; } } diff --git a/java/com/google/turbine/types/Erasure.java b/java/com/google/turbine/types/Erasure.java index aa55673..e2c7d8f 100644 --- a/java/com/google/turbine/types/Erasure.java +++ b/java/com/google/turbine/types/Erasure.java @@ -19,8 +19,13 @@ package com.google.turbine.types; import com.google.common.base.Function; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.bound.SourceTypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; import com.google.turbine.binder.sym.TyVarSymbol; import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ArrayTy; +import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.IntersectionTy; import com.google.turbine.type.Type.TyVar; /** Generic type erasure. */ @@ -36,37 +41,38 @@ public class Erasure { return eraseArrayTy((Type.ArrayTy) ty, tenv); case TY_VAR: return eraseTyVar((TyVar) ty, tenv); + case INTERSECTION_TY: + return eraseIntersectionTy((Type.IntersectionTy) ty, tenv); default: throw new AssertionError(ty.tyKind()); } } + private static Type eraseIntersectionTy( + IntersectionTy ty, Function<TyVarSymbol, TyVarInfo> tenv) { + return erase(ty.bounds().get(0), tenv); + } + private static Type eraseTyVar( TyVar ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) { SourceTypeBoundClass.TyVarInfo info = tenv.apply(ty.sym()); - if (info.superClassBound() != null) { - return erase(info.superClassBound(), tenv); - } - if (!info.interfaceBounds().isEmpty()) { - return erase(info.interfaceBounds().get(0), tenv); - } - return Type.ClassTy.OBJECT; + return erase(info.bound(), tenv); } private static Type.ArrayTy eraseArrayTy( Type.ArrayTy ty, Function<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> tenv) { - return new Type.ArrayTy(erase(ty.elementType(), tenv), ty.annos()); + return ArrayTy.create(erase(ty.elementType(), tenv), ty.annos()); } public static Type.ClassTy eraseClassTy(Type.ClassTy ty) { ImmutableList.Builder<Type.ClassTy.SimpleClassTy> classes = ImmutableList.builder(); - for (Type.ClassTy.SimpleClassTy c : ty.classes) { + for (Type.ClassTy.SimpleClassTy c : ty.classes()) { if (c.targs().isEmpty()) { classes.add(c); } else { - classes.add(new Type.ClassTy.SimpleClassTy(c.sym(), ImmutableList.of(), c.annos())); + classes.add(SimpleClassTy.create(c.sym(), ImmutableList.of(), c.annos())); } } - return new Type.ClassTy(classes.build()); + return ClassTy.create(classes.build()); } } diff --git a/javatests/com/google/turbine/binder/BinderErrorTest.java b/javatests/com/google/turbine/binder/BinderErrorTest.java index 0f37537..f6fb317 100644 --- a/javatests/com/google/turbine/binder/BinderErrorTest.java +++ b/javatests/com/google/turbine/binder/BinderErrorTest.java @@ -21,13 +21,13 @@ import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; import static org.junit.Assert.fail; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.turbine.diag.TurbineError; import com.google.turbine.parse.Parser; import com.google.turbine.tree.Tree.CompUnit; import java.util.Arrays; import java.util.Collections; +import java.util.Optional; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.Parameterized; @@ -59,11 +59,10 @@ public class BinderErrorTest { "class B extends A.NoSuch {", "}", }, - // TODO(cushon): we'd prefer the caret at NoSuch instead of A { "<>:4: error: symbol not found a.A$NoSuch", // "class B extends A.NoSuch {", - " ^", + " ^", } }, { @@ -84,7 +83,7 @@ public class BinderErrorTest { "@Anno(foo=100, bar=200) class Test {}", }, { - "<>:2: error: could not resolve foo", // + "<>:2: error: could not resolve element foo() in Anno", // "@Anno(foo=100, bar=200) class Test {}", " ^", }, @@ -95,7 +94,7 @@ public class BinderErrorTest { "@Anno(foo=100, bar=200) class Test {}", }, { - "<>:2: error: could not resolve bar", // + "<>:2: error: could not resolve element bar() in Anno", // "@Anno(foo=100, bar=200) class Test {}", " ^", }, @@ -149,7 +148,10 @@ public class BinderErrorTest { "<>:4: error: cycle in class hierarchy: p.OuterExtendsInner$Inner" + " -> p.OuterExtendsInner$Inner", " public static class Inner extends Foo {}", - " ^" + " ^", + "<>:4: error: could not resolve Foo", + " public static class Inner extends Foo {}", + " ^", }, }, { @@ -162,7 +164,10 @@ public class BinderErrorTest { { "<>:2: error: symbol not found java.lang.NoSuch", // "import java.lang.NoSuch;", - " ^" + " ^", + "<>:3: error: could not resolve NoSuch", + "public class Test extends NoSuch {", + " ^" }, }, { @@ -175,7 +180,10 @@ public class BinderErrorTest { { "<>:2: error: symbol not found java.util.List$NoSuch", // "import java.util.List.NoSuch;", - " ^" + " ^", + "<>:3: error: could not resolve NoSuch", + "public class Test extends NoSuch {", + " ^", }, }, { @@ -288,7 +296,7 @@ public class BinderErrorTest { { "<>:2: error: symbol not found java.lang.Deprecated$NoSuch", // " @Deprecated.NoSuch int x;", - " ^", + " ^", }, }, { @@ -403,6 +411,83 @@ public class BinderErrorTest { " ^", }, }, + { + { + "class Cycle extends Cycle {", // + " NoSuch f;", + "}", + }, + { + "<>:2: error: could not resolve NoSuch", // + " NoSuch f;", + " ^", + }, + }, + { + { + "@interface Anno { int foo() default 0; }", // + "@Anno(Foo.CONST)", + "class Foo {", + " static final int CONST = 42;", + "}", + }, + { + "<>:2: error: could not resolve element value() in Anno", // + "@Anno(Foo.CONST)", + " ^", + }, + }, + { + { + "@interface Anno { int foo() default 0; }", // + "@Anno(foo = Foo.)", + "class Foo {}", + }, + { + "<>:2: error: invalid annotation argument", // + "@Anno(foo = Foo.)", + " ^", + }, + }, + { + { + "import java.util.Map;", // + "class Foo {", + " Map.Entry.NoSuch<List> ys;", + "}", + }, + { + "<>:3: error: symbol not found java.util.Map$Entry$NoSuch", // + " Map.Entry.NoSuch<List> ys;", + " ^", + }, + }, + { + { + "import java.util.List;", // + "class Foo {", + " NoSuch<List> xs;", + "}", + }, + { + "<>:3: error: could not resolve NoSuch", // + " NoSuch<List> xs;", + " ^", + }, + }, + { + { + "import java.util.List;", // + "class Foo {", + " java.util.NoSuch<List> xs;", + "}", + }, + { + "<>:3: error: could not resolve java.util.NoSuch", // + " java.util.NoSuch<List> xs;", + " ^", + }, + }, }; return Arrays.asList((Object[][]) testCases); } @@ -422,9 +507,9 @@ public class BinderErrorTest { ImmutableList.of(parseLines(source)), ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); - fail(); + fail(Joiner.on('\n').join(source)); } catch (TurbineError e) { assertThat(e.getMessage()).isEqualTo(lines(expected)); } diff --git a/javatests/com/google/turbine/binder/BinderTest.java b/javatests/com/google/turbine/binder/BinderTest.java index 700baf0..8bf4cd3 100644 --- a/javatests/com/google/turbine/binder/BinderTest.java +++ b/javatests/com/google/turbine/binder/BinderTest.java @@ -16,29 +16,31 @@ package com.google.turbine.binder; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; import static org.junit.Assert.fail; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.turbine.binder.bound.SourceTypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; import com.google.turbine.binder.sym.ClassSymbol; import com.google.turbine.diag.TurbineError; import com.google.turbine.lower.IntegrationTestSupport; +import com.google.turbine.model.TurbineElementType; import com.google.turbine.model.TurbineFlag; import com.google.turbine.parse.Parser; import com.google.turbine.tree.Tree; import java.io.OutputStream; -import java.lang.annotation.ElementType; import java.nio.file.Files; import java.nio.file.Path; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.junit.Rule; @@ -76,7 +78,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); assertThat(bound.keySet()) @@ -122,7 +124,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); assertThat(bound.keySet()) @@ -162,7 +164,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); assertThat(bound.get(new ClassSymbol("other/Foo")).superclass()) @@ -192,7 +194,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()); + /* moduleVersion=*/ Optional.empty()); fail(); } catch (TurbineError e) { assertThat(e.getMessage()).contains("cycle in class hierarchy: a.A -> b.B -> a.A"); @@ -213,7 +215,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); SourceTypeBoundClass a = bound.get(new ClassSymbol("com/test/Annotation")); @@ -242,7 +244,7 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(Collections.emptyList()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); SourceTypeBoundClass a = bound.get(new ClassSymbol("a/A")); @@ -282,11 +284,39 @@ public class BinderTest { units, ClassPathBinder.bindClasspath(ImmutableList.of(libJar)), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()) + /* moduleVersion=*/ Optional.empty()) .units(); SourceTypeBoundClass a = bound.get(new ClassSymbol("C$A")); - assertThat(a.annotationMetadata().target()).containsExactly(ElementType.TYPE_USE); + assertThat(a.annotationMetadata().target()).containsExactly(TurbineElementType.TYPE_USE); + } + + // Test that we don't crash on invalid constant field initializers. + // (Error reporting is deferred to javac.) + @Test + public void invalidConst() throws Exception { + List<Tree.CompUnit> units = new ArrayList<>(); + units.add( + parseLines( + "package a;", // + "public class A {", + " public static final boolean b = true == 42;", + "}")); + + ImmutableMap<ClassSymbol, SourceTypeBoundClass> bound = + Binder.bind( + units, + ClassPathBinder.bindClasspath(Collections.emptyList()), + TURBINE_BOOTCLASSPATH, + /* moduleVersion=*/ Optional.empty()) + .units(); + + assertThat(bound.keySet()).containsExactly(new ClassSymbol("a/A")); + + SourceTypeBoundClass a = bound.get(new ClassSymbol("a/A")); + FieldInfo f = getOnlyElement(a.fields()); + assertThat(f.name()).isEqualTo("b"); + assertThat(f.value()).isNull(); } private Tree.CompUnit parseLines(String... lines) { diff --git a/javatests/com/google/turbine/binder/ClassPathBinderTest.java b/javatests/com/google/turbine/binder/ClassPathBinderTest.java index bd9bde3..0b063d4 100644 --- a/javatests/com/google/turbine/binder/ClassPathBinderTest.java +++ b/javatests/com/google/turbine/binder/ClassPathBinderTest.java @@ -16,6 +16,9 @@ package com.google.turbine.binder; +import static com.google.common.collect.Iterables.getLast; +import static com.google.common.collect.Iterables.getOnlyElement; +import static com.google.common.collect.MoreCollectors.onlyElement; import static com.google.common.truth.Truth.assertThat; import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; import static java.nio.charset.StandardCharsets.UTF_8; @@ -24,20 +27,24 @@ import static org.junit.Assert.fail; import com.google.common.base.VerifyException; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; -import com.google.turbine.binder.bound.HeaderBoundClass; +import com.google.turbine.binder.bound.EnumConstantValue; +import com.google.turbine.binder.bound.TypeBoundClass; import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.env.Env; import com.google.turbine.binder.lookup.LookupKey; import com.google.turbine.binder.lookup.LookupResult; import com.google.turbine.binder.lookup.Scope; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.binder.sym.FieldSymbol; import com.google.turbine.model.TurbineFlag; import com.google.turbine.model.TurbineTyKind; +import com.google.turbine.tree.Tree.Ident; +import com.google.turbine.type.AnnoInfo; +import com.google.turbine.type.Type.ClassTy; import java.io.IOError; import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; -import java.util.Arrays; import org.junit.Rule; import org.junit.Test; import org.junit.rules.TemporaryFolder; @@ -54,11 +61,11 @@ public class ClassPathBinderTest { Scope javaLang = TURBINE_BOOTCLASSPATH.index().lookupPackage(ImmutableList.of("java", "lang")); - LookupResult result = javaLang.lookup(new LookupKey(Arrays.asList("String"))); + LookupResult result = javaLang.lookup(new LookupKey(ImmutableList.of(new Ident(-1, "String")))); assertThat(result.remaining()).isEmpty(); assertThat(result.sym()).isEqualTo(new ClassSymbol("java/lang/String")); - result = javaLang.lookup(new LookupKey(Arrays.asList("Object"))); + result = javaLang.lookup(new LookupKey(ImmutableList.of(new Ident(-1, "Object")))); assertThat(result.remaining()).isEmpty(); assertThat(result.sym()).isEqualTo(new ClassSymbol("java/lang/Object")); } @@ -67,7 +74,7 @@ public class ClassPathBinderTest { public void classPathClasses() throws IOException { Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env(); - HeaderBoundClass c = env.get(new ClassSymbol("java/util/Map$Entry")); + TypeBoundClass c = env.get(new ClassSymbol("java/util/Map$Entry")); assertThat(c.owner()).isEqualTo(new ClassSymbol("java/util/Map")); assertThat(c.kind()).isEqualTo(TurbineTyKind.INTERFACE); @@ -85,6 +92,39 @@ public class ClassPathBinderTest { } @Test + public void interfaces() { + Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env(); + + TypeBoundClass c = env.get(new ClassSymbol("java/lang/annotation/Retention")); + assertThat(c.interfaceTypes()).hasSize(1); + assertThat(((ClassTy) getOnlyElement(c.interfaceTypes())).sym()) + .isEqualTo(new ClassSymbol("java/lang/annotation/Annotation")); + + c = env.get(new ClassSymbol("java/util/ArrayList")); + ClassTy listInterface = + (ClassTy) + c.interfaceTypes().stream() + .filter(i -> ((ClassTy) i).sym().equals(new ClassSymbol("java/util/List"))) + .collect(onlyElement()); + assertThat(getLast(listInterface.classes()).targs()).hasSize(1); + } + + @Test + public void annotations() { + Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env(); + TypeBoundClass c = env.get(new ClassSymbol("java/lang/annotation/Retention")); + + AnnoInfo anno = + c.annotations().stream() + .filter(a -> a.sym().equals(new ClassSymbol("java/lang/annotation/Retention"))) + .collect(onlyElement()); + assertThat(anno.values().keySet()).containsExactly("value"); + assertThat(((EnumConstantValue) anno.values().get("value")).sym()) + .isEqualTo( + new FieldSymbol(new ClassSymbol("java/lang/annotation/RetentionPolicy"), "RUNTIME")); + } + + @Test public void byteCodeBoundClassName() { BytecodeBoundClass c = new BytecodeBoundClass( diff --git a/javatests/com/google/turbine/binder/JimageClassBinderTest.java b/javatests/com/google/turbine/binder/JimageClassBinderTest.java index ffbbf87..bbcb245 100644 --- a/javatests/com/google/turbine/binder/JimageClassBinderTest.java +++ b/javatests/com/google/turbine/binder/JimageClassBinderTest.java @@ -16,6 +16,7 @@ package com.google.turbine.binder; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import com.google.common.collect.ImmutableList; @@ -23,6 +24,7 @@ import com.google.turbine.binder.bytecode.BytecodeBoundClass; import com.google.turbine.binder.lookup.LookupKey; import com.google.turbine.binder.lookup.LookupResult; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.tree.Tree.Ident; import java.io.IOException; import org.junit.Test; import org.junit.runner.RunWith; @@ -49,7 +51,7 @@ public class JimageClassBinderTest { binder .index() .lookupPackage(ImmutableList.of("java", "lang")) - .lookup(new LookupKey(ImmutableList.of("Object"))); + .lookup(new LookupKey(ImmutableList.of(new Ident(-1, "Object")))); assertThat(((ClassSymbol) objectSym.sym()).binaryName()).isEqualTo("java/lang/Object"); assertThat(objectSym.remaining()).isEmpty(); @@ -57,16 +59,22 @@ public class JimageClassBinderTest { binder .index() .lookupPackage(ImmutableList.of("java", "util")) - .lookup(new LookupKey(ImmutableList.of("Map", "Entry"))); + .lookup(new LookupKey(ImmutableList.of(new Ident(-1, "Map"), new Ident(-1, "Entry")))); assertThat(((ClassSymbol) entrySym.sym()).binaryName()).isEqualTo("java/util/Map"); - assertThat(entrySym.remaining()).containsExactly("Entry"); + assertThat(getOnlyElement(entrySym.remaining()).value()).isEqualTo("Entry"); entrySym = binder .index() .scope() - .lookup(new LookupKey(ImmutableList.of("java", "util", "Map", "Entry"))); + .lookup( + new LookupKey( + ImmutableList.of( + new Ident(-1, "java"), + new Ident(-1, "util"), + new Ident(-1, "Map"), + new Ident(-1, "Entry")))); assertThat(((ClassSymbol) entrySym.sym()).binaryName()).isEqualTo("java/util/Map"); - assertThat(entrySym.remaining()).containsExactly("Entry"); + assertThat(getOnlyElement(entrySym.remaining()).value()).isEqualTo("Entry"); } } diff --git a/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java new file mode 100644 index 0000000..4e3ec9a --- /dev/null +++ b/javatests/com/google/turbine/binder/bytecode/BytecodeBoundClassTest.java @@ -0,0 +1,149 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.binder.bytecode; + +import static com.google.common.collect.Iterables.getLast; +import static com.google.common.collect.MoreCollectors.onlyElement; +import static com.google.common.truth.Truth.assertThat; +import static com.google.turbine.testing.TestClassPaths.TURBINE_BOOTCLASSPATH; +import static java.util.Objects.requireNonNull; + +import com.google.common.collect.ImmutableMap; +import com.google.common.io.ByteStreams; +import com.google.turbine.binder.bound.ClassValue; +import com.google.turbine.binder.bound.TypeBoundClass; +import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; +import com.google.turbine.binder.env.CompoundEnv; +import com.google.turbine.binder.env.Env; +import com.google.turbine.binder.env.SimpleEnv; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; +import java.io.IOException; +import java.io.InputStream; +import java.io.Serializable; +import java.io.UncheckedIOException; +import java.util.List; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class BytecodeBoundClassTest { + + static class NoInterfaces {} + + abstract static class RawInterfaces implements Serializable {} + + abstract static class GenericInterfaces implements List<String> {} + + @Test + public void interfaceTypes() { + TypeBoundClass noInterfaces = getBytecodeBoundClass(NoInterfaces.class); + TypeBoundClass rawInterfaces = getBytecodeBoundClass(RawInterfaces.class); + TypeBoundClass genericInterfaces = getBytecodeBoundClass(GenericInterfaces.class); + + assertThat(noInterfaces.interfaceTypes()).isEmpty(); + + assertThat(rawInterfaces.interfaceTypes()).hasSize(1); + assertThat(((ClassTy) rawInterfaces.interfaceTypes().get(0)).sym()) + .isEqualTo(new ClassSymbol("java/io/Serializable")); + assertThat(getLast(((ClassTy) rawInterfaces.interfaceTypes().get(0)).classes()).targs()) + .isEmpty(); + + assertThat(genericInterfaces.interfaceTypes()).hasSize(1); + assertThat(((ClassTy) genericInterfaces.interfaceTypes().get(0)).sym()) + .isEqualTo(new ClassSymbol("java/util/List")); + assertThat(getLast(((ClassTy) genericInterfaces.interfaceTypes().get(0)).classes()).targs()) + .hasSize(1); + assertThat( + ((ClassTy) + getLast(((ClassTy) genericInterfaces.interfaceTypes().get(0)).classes()) + .targs() + .get(0)) + .sym()) + .isEqualTo(new ClassSymbol("java/lang/String")); + } + + static class HasMethod { + @Deprecated + <X, Y extends X, Z extends Throwable> X foo(@Deprecated X bar, Y baz) throws IOException, Z { + return null; + } + } + + @Test + public void methodTypes() { + MethodInfo m = + getBytecodeBoundClass(HasMethod.class).methods().stream() + .filter(x -> x.name().equals("foo")) + .collect(onlyElement()); + + assertThat(m.tyParams()).hasSize(3); + assertThat(m.parameters().get(0).annotations()).hasSize(1); + assertThat(m.parameters().get(0).name()).isEqualTo("bar"); + assertThat(m.exceptions()).hasSize(2); + } + + @interface VoidAnno { + Class<?> a() default void.class; + + Class<?> b() default int[].class; + } + + @Test + public void voidAnno() { + BytecodeBoundClass c = getBytecodeBoundClass(VoidAnno.class); + + assertThat(c.methods()).hasSize(2); + assertThat(((ClassValue) c.methods().get(0).defaultValue()).type().tyKind()) + .isEqualTo(Type.TyKind.VOID_TY); + assertThat(((ClassValue) c.methods().get(1).defaultValue()).type().tyKind()) + .isEqualTo(Type.TyKind.ARRAY_TY); + } + + private static byte[] toByteArrayOrDie(InputStream is) { + try { + return ByteStreams.toByteArray(is); + } catch (IOException e) { + throw new UncheckedIOException(e); + } + } + + private BytecodeBoundClass getBytecodeBoundClass( + Env<ClassSymbol, BytecodeBoundClass> env, Class<?> clazz) { + String name = clazz.getName().replace('.', '/'); + String path = "/" + name + ".class"; + return new BytecodeBoundClass( + new ClassSymbol(name), + () -> toByteArrayOrDie(requireNonNull(getClass().getResourceAsStream(path), path)), + env, + "test.jar"); + } + + private BytecodeBoundClass getBytecodeBoundClass(Class<?> clazz) { + Env<ClassSymbol, BytecodeBoundClass> env = TURBINE_BOOTCLASSPATH.env(); + env = + CompoundEnv.of(env) + .append( + new SimpleEnv<>( + ImmutableMap.of( + new ClassSymbol(BytecodeBoundClass.class.getName().replace('.', '/')), + getBytecodeBoundClass(env, BytecodeBoundClassTest.class)))); + return getBytecodeBoundClass(env, clazz); + } +} diff --git a/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java b/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java index 3dcf127..022e47c 100644 --- a/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java +++ b/javatests/com/google/turbine/binder/lookup/TopLevelIndexTest.java @@ -16,11 +16,13 @@ package com.google.turbine.binder.lookup; +import static com.google.common.collect.Iterables.getOnlyElement; import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.tree.Tree.Ident; import java.util.NoSuchElementException; import org.junit.Test; import org.junit.runner.RunWith; @@ -36,13 +38,12 @@ public class TopLevelIndexTest { ImmutableList.of( new ClassSymbol("java/util/Map"), new ClassSymbol("java/util/List"), - new ClassSymbol("com/google/common/base/Optional"))); + new ClassSymbol("java.util.Optional"))); } @Test public void simple() { - LookupResult result = - index.scope().lookup(new LookupKey(ImmutableList.of("java", "util", "Map"))); + LookupResult result = index.scope().lookup(lookupKey(ImmutableList.of("java", "util", "Map"))); assertThat(result.sym()).isEqualTo(new ClassSymbol("java/util/Map")); assertThat(result.remaining()).isEmpty(); } @@ -50,14 +51,14 @@ public class TopLevelIndexTest { @Test public void nested() { LookupResult result = - index.scope().lookup(new LookupKey(ImmutableList.of("java", "util", "Map", "Entry"))); + index.scope().lookup(lookupKey(ImmutableList.of("java", "util", "Map", "Entry"))); assertThat(result.sym()).isEqualTo(new ClassSymbol("java/util/Map")); - assertThat(result.remaining()).containsExactly("Entry"); + assertThat(getOnlyElement(result.remaining()).value()).isEqualTo("Entry"); } @Test public void empty() { - assertThat(index.scope().lookup(new LookupKey(ImmutableList.of("java", "NoSuch", "Entry")))) + assertThat(index.scope().lookup(lookupKey(ImmutableList.of("java", "NoSuch", "Entry")))) .isNull(); assertThat(index.lookupPackage(ImmutableList.of("java", "math"))).isNull(); assertThat(index.lookupPackage(ImmutableList.of("java", "util", "Map"))).isNull(); @@ -67,11 +68,11 @@ public class TopLevelIndexTest { public void packageScope() { Scope scope = index.lookupPackage(ImmutableList.of("java", "util")); - assertThat(scope.lookup(new LookupKey(ImmutableList.of("Map"))).sym()) + assertThat(scope.lookup(lookupKey(ImmutableList.of("Map"))).sym()) .isEqualTo(new ClassSymbol("java/util/Map")); - assertThat(scope.lookup(new LookupKey(ImmutableList.of("List"))).sym()) + assertThat(scope.lookup(lookupKey(ImmutableList.of("List"))).sym()) .isEqualTo(new ClassSymbol("java/util/List")); - assertThat(scope.lookup(new LookupKey(ImmutableList.of("NoSuch")))).isNull(); + assertThat(scope.lookup(lookupKey(ImmutableList.of("NoSuch")))).isNull(); } @Test @@ -82,7 +83,7 @@ public class TopLevelIndexTest { SimpleTopLevelIndex.of( ImmutableList.of(new ClassSymbol("java/Foo"), new ClassSymbol("java/Foo/Bar"))); - LookupResult result = index.scope().lookup(new LookupKey(ImmutableList.of("java", "Foo"))); + LookupResult result = index.scope().lookup(lookupKey(ImmutableList.of("java", "Foo"))); assertThat(result.sym()).isEqualTo(new ClassSymbol("java/Foo")); assertThat(result.remaining()).isEmpty(); } @@ -92,11 +93,11 @@ public class TopLevelIndexTest { SimpleTopLevelIndex.of( ImmutableList.of(new ClassSymbol("java/Foo/Bar"), new ClassSymbol("java/Foo"))); - assertThat(index.scope().lookup(new LookupKey(ImmutableList.of("java", "Foo")))).isNull(); + assertThat(index.scope().lookup(lookupKey(ImmutableList.of("java", "Foo")))).isNull(); LookupResult packageResult = index .lookupPackage(ImmutableList.of("java", "Foo")) - .lookup(new LookupKey(ImmutableList.of("Bar"))); + .lookup(lookupKey(ImmutableList.of("Bar"))); assertThat(packageResult.sym()).isEqualTo(new ClassSymbol("java/Foo/Bar")); assertThat(packageResult.remaining()).isEmpty(); } @@ -104,7 +105,7 @@ public class TopLevelIndexTest { @Test public void emptyLookup() { - LookupKey key = new LookupKey(ImmutableList.of("java", "util", "List")); + LookupKey key = lookupKey(ImmutableList.of("java", "util", "List")); key = key.rest(); key = key.rest(); try { @@ -114,4 +115,12 @@ public class TopLevelIndexTest { // expected } } + + private LookupKey lookupKey(ImmutableList<String> names) { + ImmutableList.Builder<Ident> result = ImmutableList.builder(); + for (String name : names) { + result.add(new Ident(-1, name)); + } + return new LookupKey(result.build()); + } } diff --git a/javatests/com/google/turbine/bytecode/ClassReaderTest.java b/javatests/com/google/turbine/bytecode/ClassReaderTest.java index 5bce03d..dda29ac 100644 --- a/javatests/com/google/turbine/bytecode/ClassReaderTest.java +++ b/javatests/com/google/turbine/bytecode/ClassReaderTest.java @@ -133,7 +133,6 @@ public class ClassReaderTest { assertThat(classFile.annotations()).hasSize(1); ClassFile.AnnotationInfo annotation = Iterables.getOnlyElement(classFile.annotations()); assertThat(annotation.typeName()).isEqualTo("Ljava/lang/annotation/Retention;"); - assertThat(annotation.isRuntimeVisible()).isTrue(); assertThat(annotation.elementValuePairs()).hasSize(1); assertThat(annotation.elementValuePairs()).containsKey("value"); ElementValue value = annotation.elementValuePairs().get("value"); diff --git a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java index 1c0d5c4..f3ab8e7 100644 --- a/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java +++ b/javatests/com/google/turbine/bytecode/sig/SigIntegrationTest.java @@ -93,7 +93,7 @@ public class SigIntegrationTest { try { new ClassReader(Files.newInputStream(path)) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public void visit( int version, diff --git a/javatests/com/google/turbine/deps/DependenciesTest.java b/javatests/com/google/turbine/deps/DependenciesTest.java index 70377fb..4c2362f 100644 --- a/javatests/com/google/turbine/deps/DependenciesTest.java +++ b/javatests/com/google/turbine/deps/DependenciesTest.java @@ -19,7 +19,6 @@ package com.google.turbine.deps; import static com.google.common.truth.Truth.assertThat; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.collect.ImmutableSet; @@ -44,6 +43,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import java.util.stream.Collectors; @@ -106,7 +106,7 @@ public class DependenciesTest { units, ClassPathBinder.bindClasspath(classpath), TestClassPaths.TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()); + /* moduleVersion=*/ Optional.empty()); Lowered lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()); diff --git a/javatests/com/google/turbine/lower/IntegrationTestSupport.java b/javatests/com/google/turbine/lower/IntegrationTestSupport.java index 51b5a2e..c039241 100644 --- a/javatests/com/google/turbine/lower/IntegrationTestSupport.java +++ b/javatests/com/google/turbine/lower/IntegrationTestSupport.java @@ -23,7 +23,6 @@ import static java.util.stream.Collectors.toCollection; import static java.util.stream.Collectors.toList; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.base.Splitter; import com.google.common.collect.ImmutableList; import com.google.common.jimfs.Configuration; @@ -60,6 +59,7 @@ import java.util.HashSet; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.Set; import javax.tools.DiagnosticCollector; import javax.tools.JavaFileObject; @@ -404,7 +404,7 @@ public class IntegrationTestSupport { final Set<String> classes1 = classes; new SignatureReader(signature) .accept( - new SignatureVisitor(Opcodes.ASM6) { + new SignatureVisitor(Opcodes.ASM7) { private final Set<String> classes = classes1; // class signatures may contain type arguments that contain class signatures Deque<List<String>> pieces = new ArrayDeque<>(); @@ -431,7 +431,7 @@ public class IntegrationTestSupport { static Map<String, byte[]> runTurbine(Map<String, String> input, ImmutableList<Path> classpath) throws IOException { return runTurbine( - input, classpath, TURBINE_BOOTCLASSPATH, /* moduleVersion= */ Optional.absent()); + input, classpath, TURBINE_BOOTCLASSPATH, /* moduleVersion= */ Optional.empty()); } static Map<String, byte[]> runTurbine( diff --git a/javatests/com/google/turbine/lower/LowerIntegrationTest.java b/javatests/com/google/turbine/lower/LowerIntegrationTest.java index 6496756..f7e9c18 100644 --- a/javatests/com/google/turbine/lower/LowerIntegrationTest.java +++ b/javatests/com/google/turbine/lower/LowerIntegrationTest.java @@ -231,7 +231,7 @@ public class LowerIntegrationTest { "import_wild_order.test", "canon_recursive.test", // TODO(cushon): crashes ASM, see: - // http://forge.ow2.org/tracker/?func=detail&aid=317776&group_id=23&atid=100023 + // https://gitlab.ow2.org/asm/asm/issues/317776 // "canon_array.test", "java_lang_object.test", "visible_package.test", @@ -303,6 +303,11 @@ public class LowerIntegrationTest { // TODO(cushon): support for source level 9 in integration tests // "B74332665.test", "memberimport.test", + "type_anno_c_array.test", + // https://bugs.openjdk.java.net/browse/JDK-8054064 ? + "shadow_inherited.test", + "static_final_boxed.test", + "anno_void.test", }; List<Object[]> tests = ImmutableList.copyOf(testCases).stream().map(x -> new Object[] {x}).collect(toList()); diff --git a/javatests/com/google/turbine/lower/LowerSignatureTest.java b/javatests/com/google/turbine/lower/LowerSignatureTest.java index 59698e3..5ccbf01 100644 --- a/javatests/com/google/turbine/lower/LowerSignatureTest.java +++ b/javatests/com/google/turbine/lower/LowerSignatureTest.java @@ -25,6 +25,13 @@ import com.google.turbine.bytecode.sig.SigWriter; import com.google.turbine.model.TurbineConstantTypeKind; import com.google.turbine.type.Type; import com.google.turbine.type.Type.ArrayTy; +import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.PrimTy; +import com.google.turbine.type.Type.TyVar; +import com.google.turbine.type.Type.WildLowerBoundedTy; +import com.google.turbine.type.Type.WildUnboundedTy; +import com.google.turbine.type.Type.WildUpperBoundedTy; import org.junit.Test; import org.junit.runner.RunWith; import org.junit.runners.JUnit4; @@ -34,9 +41,9 @@ public class LowerSignatureTest { @Test public void simple() { Type.ClassTy type = - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("java/util/List"), ImmutableList.of(), ImmutableList.of()))); assertThat(SigWriter.type(new LowerSignature().signature(type))).isEqualTo("Ljava/util/List;"); } @@ -47,13 +54,13 @@ public class LowerSignatureTest { SigWriter.type( new LowerSignature() .signature( - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("test/Outer"), ImmutableList.of(), ImmutableList.of()), - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("test/Outer$Inner"), ImmutableList.of(), ImmutableList.of())))))) @@ -63,15 +70,15 @@ public class LowerSignatureTest { @Test public void genericEnclosing() { Type.ClassTy type = - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("test/Outer"), - ImmutableList.of(Type.ClassTy.OBJECT), + ImmutableList.of(ClassTy.OBJECT), ImmutableList.of()), - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("test/Outer$Inner"), - ImmutableList.of(Type.ClassTy.OBJECT), + ImmutableList.of(ClassTy.OBJECT), ImmutableList.of()))); assertThat(SigWriter.type(new LowerSignature().signature(type))) .isEqualTo("Ltest/Outer<Ljava/lang/Object;>.Inner<Ljava/lang/Object;>;"); @@ -85,13 +92,13 @@ public class LowerSignatureTest { SigWriter.type( new LowerSignature() .signature( - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("Outer"), ImmutableList.of(), ImmutableList.of()), - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("Outer$Inner"), ImmutableList.of(), ImmutableList.of())))))) @@ -104,16 +111,16 @@ public class LowerSignatureTest { SigWriter.type( new LowerSignature() .signature( - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("test/Test"), ImmutableList.of( - new Type.WildUnboundedTy(ImmutableList.of()), - new Type.WildLowerBoundedTy( - Type.ClassTy.OBJECT, ImmutableList.of()), - new Type.WildUpperBoundedTy( - Type.ClassTy.OBJECT, ImmutableList.of())), + WildUnboundedTy.create(ImmutableList.of()), + WildLowerBoundedTy.create( + ClassTy.OBJECT, ImmutableList.of()), + WildUpperBoundedTy.create( + ClassTy.OBJECT, ImmutableList.of())), ImmutableList.of())))))) .isEqualTo("Ltest/Test<*-Ljava/lang/Object;+Ljava/lang/Object;>;"); } @@ -124,7 +131,7 @@ public class LowerSignatureTest { SigWriter.type( new LowerSignature() .signature( - new Type.TyVar( + TyVar.create( new TyVarSymbol(ClassSymbol.OBJECT, "X"), ImmutableList.of())))) .isEqualTo("TX;"); } @@ -134,8 +141,7 @@ public class LowerSignatureTest { assertThat( SigWriter.type( new LowerSignature() - .signature( - new Type.PrimTy(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of())))) + .signature(PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of())))) .isEqualTo("Z"); } @@ -150,10 +156,10 @@ public class LowerSignatureTest { SigWriter.type( new LowerSignature() .signature( - new Type.ArrayTy( - new ArrayTy( - new ArrayTy( - new Type.PrimTy( + ArrayTy.create( + ArrayTy.create( + ArrayTy.create( + PrimTy.create( TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()), ImmutableList.of()), ImmutableList.of()), diff --git a/javatests/com/google/turbine/lower/LowerTest.java b/javatests/com/google/turbine/lower/LowerTest.java index d97f46a..ef070f9 100644 --- a/javatests/com/google/turbine/lower/LowerTest.java +++ b/javatests/com/google/turbine/lower/LowerTest.java @@ -22,7 +22,6 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static org.junit.Assert.fail; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableMap; import com.google.common.io.ByteStreams; @@ -44,6 +43,11 @@ import com.google.turbine.model.TurbineTyKind; import com.google.turbine.parse.Parser; import com.google.turbine.testing.AsmUtils; import com.google.turbine.type.Type; +import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.ClassTy.SimpleClassTy; +import com.google.turbine.type.Type.IntersectionTy; +import com.google.turbine.type.Type.PrimTy; +import com.google.turbine.type.Type.TyVar; import java.io.IOException; import java.io.OutputStream; import java.nio.file.Files; @@ -52,6 +56,7 @@ import java.util.ArrayList; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.junit.Rule; @@ -75,14 +80,14 @@ public class LowerTest { @Test public void hello() throws Exception { - ImmutableList<Type.ClassTy> interfaceTypes = + ImmutableList<Type> interfaceTypes = ImmutableList.of( - new Type.ClassTy( + ClassTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( + SimpleClassTy.create( new ClassSymbol("java/util/List"), ImmutableList.of( - new Type.TyVar( + TyVar.create( new TyVarSymbol(new ClassSymbol("test/Test"), "V"), ImmutableList.of())), ImmutableList.of())))); @@ -91,13 +96,14 @@ public class LowerTest { ImmutableMap.of( new TyVarSymbol(new ClassSymbol("test/Test"), "V"), new SourceTypeBoundClass.TyVarInfo( - new Type.ClassTy( + IntersectionTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( - new ClassSymbol("test/Test$Inner"), - ImmutableList.of(), - ImmutableList.of()))), - ImmutableList.of(), + ClassTy.create( + ImmutableList.of( + SimpleClassTy.create( + new ClassSymbol("test/Test$Inner"), + ImmutableList.of(), + ImmutableList.of()))))), ImmutableList.of())); int access = TurbineFlag.ACC_SUPER | TurbineFlag.ACC_PUBLIC; ImmutableList<SourceTypeBoundClass.MethodInfo> methods = @@ -105,7 +111,7 @@ public class LowerTest { new SourceTypeBoundClass.MethodInfo( new MethodSymbol(new ClassSymbol("test/Test"), "f"), ImmutableMap.of(), - new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()), + PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), ImmutableList.of(), ImmutableList.of(), TurbineFlag.ACC_STATIC | TurbineFlag.ACC_PUBLIC, @@ -118,34 +124,35 @@ public class LowerTest { ImmutableMap.of( new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "V"), new SourceTypeBoundClass.TyVarInfo( - null, - ImmutableList.of( - new Type.ClassTy( - ImmutableList.of( - new Type.ClassTy.SimpleClassTy( - new ClassSymbol("java/lang/Runnable"), - ImmutableList.of(), - ImmutableList.of())))), + IntersectionTy.create( + ImmutableList.of( + ClassTy.create( + ImmutableList.of( + SimpleClassTy.create( + new ClassSymbol("java/lang/Runnable"), + ImmutableList.of(), + ImmutableList.of()))))), ImmutableList.of()), new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"), new SourceTypeBoundClass.TyVarInfo( - new Type.ClassTy( + IntersectionTy.create( ImmutableList.of( - new Type.ClassTy.SimpleClassTy( - new ClassSymbol("java/lang/Error"), - ImmutableList.of(), - ImmutableList.of()))), - ImmutableList.of(), + ClassTy.create( + ImmutableList.of( + SimpleClassTy.create( + new ClassSymbol("java/lang/Error"), + ImmutableList.of(), + ImmutableList.of()))))), ImmutableList.of())), Type.VOID, ImmutableList.of( new SourceTypeBoundClass.ParamInfo( - new Type.PrimTy(TurbineConstantTypeKind.INT, ImmutableList.of()), + PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), "foo", ImmutableList.of(), 0)), ImmutableList.of( - new Type.TyVar( + TyVar.create( new TyVarSymbol(new MethodSymbol(new ClassSymbol("test/Test"), "g"), "E"), ImmutableList.of())), TurbineFlag.ACC_PUBLIC, @@ -185,6 +192,7 @@ public class LowerTest { null, null, ImmutableList.of(), + null, null); SourceTypeBoundClass i = @@ -204,6 +212,7 @@ public class LowerTest { null, null, ImmutableList.of(), + null, null); SimpleEnv.Builder<ClassSymbol, SourceTypeBoundClass> b = SimpleEnv.builder(); @@ -247,13 +256,13 @@ public class LowerTest { "}"))), ClassPathBinder.bindClasspath(ImmutableList.of()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()); + /* moduleVersion=*/ Optional.empty()); Map<String, byte[]> lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); List<String> attributes = new ArrayList<>(); new ClassReader(lowered.get("Test$Inner$InnerMost")) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public void visitInnerClass( String name, String outerName, String innerName, int access) { @@ -325,17 +334,17 @@ public class LowerTest { "}"))), ClassPathBinder.bindClasspath(ImmutableList.of()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()); + /* moduleVersion=*/ Optional.empty()); Map<String, byte[]> lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); TypePath[] path = new TypePath[1]; new ClassReader(lowered.get("Test")) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public FieldVisitor visitField( int access, String name, String desc, String signature, Object value) { - return new FieldVisitor(Opcodes.ASM6) { + return new FieldVisitor(Opcodes.ASM7) { @Override public AnnotationVisitor visitTypeAnnotation( int typeRef, TypePath typePath, String desc, boolean visible) { @@ -382,7 +391,7 @@ public class LowerTest { Map<String, Object> values = new LinkedHashMap<>(); new ClassReader(actual.get("Test")) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public FieldVisitor visitField( int access, String name, String desc, String signature, Object value) { @@ -403,13 +412,13 @@ public class LowerTest { ImmutableList.of(Parser.parse("@Deprecated class Test {}")), ClassPathBinder.bindClasspath(ImmutableList.of()), TURBINE_BOOTCLASSPATH, - /* moduleVersion=*/ Optional.absent()); + /* moduleVersion=*/ Optional.empty()); Map<String, byte[]> lowered = Lower.lowerAll(bound.units(), bound.modules(), bound.classPathEnv()).bytes(); int[] acc = {0}; new ClassReader(lowered.get("Test")) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public void visit( int version, @@ -594,7 +603,10 @@ public class LowerTest { } catch (TurbineError error) { assertThat(error) .hasMessageThat() - .contains("Test.java: error: could not locate class file for A"); + .contains( + "Test.java:3: error: could not locate class file for A\n" + + " I i;\n" + + " ^"); } } @@ -610,7 +622,7 @@ public class LowerTest { int[] testAccess = {0}; new ClassReader(lowered.get("Test")) .accept( - new ClassVisitor(Opcodes.ASM6) { + new ClassVisitor(Opcodes.ASM7) { @Override public void visit( int version, diff --git a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java index d1bcb74..4067bd1 100644 --- a/javatests/com/google/turbine/lower/ModuleIntegrationTest.java +++ b/javatests/com/google/turbine/lower/ModuleIntegrationTest.java @@ -21,13 +21,14 @@ import static java.nio.charset.StandardCharsets.UTF_8; import static java.util.stream.Collectors.toList; import static org.junit.Assert.assertEquals; -import com.google.common.base.Optional; import com.google.common.collect.ImmutableList; import com.google.common.io.ByteStreams; +import com.google.turbine.binder.CtSymClassBinder; import com.google.turbine.binder.JimageClassBinder; import java.nio.file.Files; import java.nio.file.Path; import java.util.Map; +import java.util.Optional; import java.util.jar.JarEntry; import java.util.jar.JarOutputStream; import org.junit.Rule; @@ -94,7 +95,12 @@ public class ModuleIntegrationTest { Map<String, byte[]> actual = IntegrationTestSupport.runTurbine( - input.sources, classpathJar, JimageClassBinder.bindDefault(), Optional.of("42")); + input.sources, + classpathJar, + Double.parseDouble(System.getProperty("java.class.version")) < 54 + ? JimageClassBinder.bindDefault() + : CtSymClassBinder.bind("9"), + Optional.of("42")); assertEquals(dump(expected), dump(actual)); } diff --git a/javatests/com/google/turbine/lower/moduletestdata/classpath.test b/javatests/com/google/turbine/lower/moduletestdata/classpath.test new file mode 100644 index 0000000..734ffea --- /dev/null +++ b/javatests/com/google/turbine/lower/moduletestdata/classpath.test @@ -0,0 +1,7 @@ +%%% module-info.java %%% +module one {} + +=== module-info.java === +module two { + requires one; +}
\ No newline at end of file diff --git a/javatests/com/google/turbine/lower/moduletestdata/module-info.test b/javatests/com/google/turbine/lower/moduletestdata/module-info.test new file mode 100644 index 0000000..cba828b --- /dev/null +++ b/javatests/com/google/turbine/lower/moduletestdata/module-info.test @@ -0,0 +1,84 @@ +=== a/A.java === +package a; +import static java.lang.annotation.ElementType.MODULE; +import java.lang.annotation.Target; +@Target(MODULE) +public @interface A { + int[] value() default {}; +} + +=== a/B.java === +package a; +import static java.lang.annotation.ElementType.MODULE; +import java.lang.annotation.Target; +@Target(MODULE) +public @interface B {} + +=== com/google/Baz.java === +package com.google; +public interface Baz { + public static int A = 42; + public static int B = 43; + public interface I { + public static final int C = 44; + } +} + +=== com/google/Foo.java === +package com.google; +public class Foo implements Baz.I { +} + +=== com/google/Bar.java === +package com.google; +public class Bar implements Baz.I { +} + +=== com/google/p1/One.java === +package com.google.p1; +public class One {} + +=== com/google/p2/Two.java === +package com.google.p2; +public class Two {} + +=== com/google/p3/Three.java === +package com.google.p3; +public class Three {} + +=== module-info.java === + +import a.A; +import a.B; +import com.google.Foo; +import com.google.Baz; +import static com.google.Baz.A; + +@A({A, Baz.B, Baz.I.C}) +@B +module com.google.m { + requires java.compiler; + requires transitive jdk.compiler; + requires static java.base; + + exports com.google.p1; + exports com.google.p2 to + java.base; + exports com.google.p3 to + java.base, + java.compiler; + + opens com.google.p1; + opens com.google.p2 to + java.base; + opens com.google.p3 to + java.base, + java.compiler; + + uses Foo; + uses com.google.Bar; + + provides com.google.Baz.I with + Foo, + com.google.Bar; +} diff --git a/javatests/com/google/turbine/lower/moduletestdata/multimodule.test b/javatests/com/google/turbine/lower/moduletestdata/multimodule.test new file mode 100644 index 0000000..6c288b0 --- /dev/null +++ b/javatests/com/google/turbine/lower/moduletestdata/multimodule.test @@ -0,0 +1,13 @@ +%%% module-info.java %%% +module one {} + +=== two/module-info.java === +module two { + requires one; +} + +=== three/module-info.java === +module three { + requires one; + requires two; +}
\ No newline at end of file diff --git a/javatests/com/google/turbine/lower/testdata/anno_void.test b/javatests/com/google/turbine/lower/testdata/anno_void.test new file mode 100644 index 0000000..280302e --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/anno_void.test @@ -0,0 +1,8 @@ +=== V.java === +@interface V { + Class<?> value() default void.class; +} +=== A.java === +@V() +class A { +}
\ No newline at end of file diff --git a/javatests/com/google/turbine/lower/testdata/golden/outer.txt b/javatests/com/google/turbine/lower/testdata/golden/outer.txt index a7868b2..b6d541c 100644 --- a/javatests/com/google/turbine/lower/testdata/golden/outer.txt +++ b/javatests/com/google/turbine/lower/testdata/golden/outer.txt @@ -2,7 +2,7 @@ // access flags 0x21 // signature <V:Ltest/Test$Inner;>Ljava/lang/Object;Ljava/util/List<TV;>; // declaration: test/Test<V extends test.Test$Inner> implements java.util.List<V> -public class test/Test implements java/util/List { +public class test/Test implements java/util/List { // access flags 0xC protected static INNERCLASS test/Test$Inner test/Test Inner diff --git a/javatests/com/google/turbine/lower/testdata/shadow_inherited.test b/javatests/com/google/turbine/lower/testdata/shadow_inherited.test new file mode 100644 index 0000000..9fbb478 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/shadow_inherited.test @@ -0,0 +1,8 @@ +=== A.java === +class A { + class B {} +} +=== B.java === +class B extends A { + B b; +}
\ No newline at end of file diff --git a/javatests/com/google/turbine/lower/testdata/static_final_boxed.test b/javatests/com/google/turbine/lower/testdata/static_final_boxed.test new file mode 100644 index 0000000..6626483 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/static_final_boxed.test @@ -0,0 +1,11 @@ +=== T.java === +class T { + public static final boolean Z = (Boolean) false; + public static final byte B = (Byte) (byte) 0; + public static final char C = (Character) (char) 0; + public static final short S = (Short) (short) 0; + public static final int I = (Integer) 0; + public static final long L = (Long) 0L; + public static final double D = (Double) 0d; + public static final float F = (Float) 0f; +}
\ No newline at end of file diff --git a/javatests/com/google/turbine/lower/testdata/type_anno_c_array.test b/javatests/com/google/turbine/lower/testdata/type_anno_c_array.test new file mode 100644 index 0000000..c8ac275 --- /dev/null +++ b/javatests/com/google/turbine/lower/testdata/type_anno_c_array.test @@ -0,0 +1,28 @@ +=== Test.java === +import java.lang.annotation.Target; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface A {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface B {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface C {} + +@Retention(RetentionPolicy.RUNTIME) +@Target(ElementType.TYPE_USE) +@interface D {} + +class Test { + static final int as @A [] = {}; + static final int bs @A [] @B [] = {}; + static final int @C [] cs @A [] @B [] = {}; + static final int @C [] @D [] ds @A [] @B [] = {}; +} diff --git a/javatests/com/google/turbine/main/MainTest.java b/javatests/com/google/turbine/main/MainTest.java index 9307cb0..654a89b 100644 --- a/javatests/com/google/turbine/main/MainTest.java +++ b/javatests/com/google/turbine/main/MainTest.java @@ -229,4 +229,17 @@ public class MainTest { assertThat(expected).hasMessageThat().contains("java.lang"); } } + + @Test + public void usage() throws IOException { + Path src = temporaryFolder.newFile("Test.java").toPath(); + Files.write(src, "public class Test {}".getBytes(UTF_8)); + + try { + Main.compile(optionsWithBootclasspath().addSources(ImmutableList.of(src.toString())).build()); + fail(); + } catch (UsageException expected) { + assertThat(expected).hasMessageThat().contains("--output is required"); + } + } } diff --git a/javatests/com/google/turbine/model/ConstTest.java b/javatests/com/google/turbine/model/ConstTest.java new file mode 100644 index 0000000..7940124 --- /dev/null +++ b/javatests/com/google/turbine/model/ConstTest.java @@ -0,0 +1,85 @@ +/* + * Copyright 2018 Google Inc. All Rights Reserved. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.turbine.model; + +import com.google.common.collect.ImmutableList; +import com.google.common.collect.ImmutableMap; +import com.google.common.testing.EqualsTester; +import com.google.turbine.binder.bound.AnnotationValue; +import com.google.turbine.binder.bound.ClassValue; +import com.google.turbine.binder.sym.ClassSymbol; +import com.google.turbine.type.Type.ClassTy; +import com.google.turbine.type.Type.PrimTy; +import org.junit.Test; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +@RunWith(JUnit4.class) +public class ConstTest { + @Test + public void equalsTest() { + new EqualsTester() + .addEqualityGroup(new Const.BooleanValue(true), new Const.BooleanValue(true)) + .addEqualityGroup(new Const.BooleanValue(false), new Const.BooleanValue(false)) + .addEqualityGroup(new Const.IntValue(1), new Const.IntValue(1)) + .addEqualityGroup(new Const.IntValue(2), new Const.IntValue(2)) + .addEqualityGroup(new Const.LongValue(1), new Const.LongValue(1)) + .addEqualityGroup(new Const.LongValue(2), new Const.LongValue(2)) + .addEqualityGroup(new Const.CharValue('x'), new Const.CharValue('x')) + .addEqualityGroup(new Const.CharValue('y'), new Const.CharValue('y')) + .addEqualityGroup(new Const.FloatValue(1), new Const.FloatValue(1)) + .addEqualityGroup(new Const.FloatValue(2), new Const.FloatValue(2)) + .addEqualityGroup(new Const.DoubleValue(1), new Const.DoubleValue(1)) + .addEqualityGroup(new Const.DoubleValue(2), new Const.DoubleValue(2)) + .addEqualityGroup(new Const.StringValue("a"), new Const.StringValue("a")) + .addEqualityGroup(new Const.StringValue("b"), new Const.StringValue("b")) + .addEqualityGroup(new Const.ShortValue((short) 1), new Const.ShortValue((short) 1)) + .addEqualityGroup(new Const.ShortValue((short) 2), new Const.ShortValue((short) 2)) + .addEqualityGroup(new Const.ByteValue((byte) 1), new Const.ByteValue((byte) 1)) + .addEqualityGroup(new Const.ByteValue((byte) 2), new Const.ByteValue((byte) 2)) + .addEqualityGroup( + new Const.ArrayInitValue( + ImmutableList.of(new Const.IntValue(1), new Const.IntValue(2))), + new Const.ArrayInitValue( + ImmutableList.of(new Const.IntValue(1), new Const.IntValue(2)))) + .addEqualityGroup( + new Const.ArrayInitValue( + ImmutableList.of(new Const.IntValue(3), new Const.IntValue(4))), + new Const.ArrayInitValue( + ImmutableList.of(new Const.IntValue(3), new Const.IntValue(4)))) + .addEqualityGroup( + new AnnotationValue( + new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3))), + new AnnotationValue( + new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(3)))) + .addEqualityGroup( + new AnnotationValue( + new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4))), + new AnnotationValue( + new ClassSymbol("test/Anno"), ImmutableMap.of("value", new Const.IntValue(4)))) + .addEqualityGroup( + new ClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz"))), + new ClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Clazz")))) + .addEqualityGroup( + new ClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Other"))), + new ClassValue(ClassTy.asNonParametricClassTy(new ClassSymbol("test/Other")))) + .addEqualityGroup( + new ClassValue(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of())), + new ClassValue(PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()))) + .testEquals(); + } +} diff --git a/javatests/com/google/turbine/options/TurbineOptionsTest.java b/javatests/com/google/turbine/options/TurbineOptionsTest.java index 36f9e20..5953bac 100644 --- a/javatests/com/google/turbine/options/TurbineOptionsTest.java +++ b/javatests/com/google/turbine/options/TurbineOptionsTest.java @@ -17,6 +17,7 @@ package com.google.turbine.options; import static com.google.common.truth.Truth.assertThat; +import static com.google.common.truth.Truth8.assertThat; import static org.junit.Assert.fail; import com.google.common.collect.ImmutableList; @@ -79,7 +80,7 @@ public class TurbineOptionsTest { TurbineOptions options = TurbineOptionsParser.parse(Iterables.concat(BASE_ARGS, Arrays.asList(lines))); - assertThat(options.outputFile()).isEqualTo("out.jar"); + assertThat(options.output()).hasValue("out.jar"); assertThat(options.sourceJars()) .containsExactly("sources1.srcjar", "sources2.srcjar") .inOrder(); @@ -181,8 +182,8 @@ public class TurbineOptionsTest { TurbineOptions options = TurbineOptionsParser.parse(Arrays.asList(lines)); - assertThat(options.targetLabel()).isAbsent(); - assertThat(options.injectingRuleKind()).isAbsent(); + assertThat(options.targetLabel()).isEmpty(); + assertThat(options.injectingRuleKind()).isEmpty(); } @Test @@ -222,13 +223,9 @@ public class TurbineOptionsTest { } @Test - public void failIfMissingExpectedArgs() throws Exception { - try { - TurbineOptions.builder().build(); - fail(); - } catch (NullPointerException e) { - assertThat(e).hasMessage("output must not be null"); - } + public void tolerateMissingOutput() throws Exception { + TurbineOptions options = TurbineOptions.builder().build(); + assertThat(options.output()).isEmpty(); } @Test diff --git a/javatests/com/google/turbine/parse/ParseErrorTest.java b/javatests/com/google/turbine/parse/ParseErrorTest.java index c728c8a..53864ef 100644 --- a/javatests/com/google/turbine/parse/ParseErrorTest.java +++ b/javatests/com/google/turbine/parse/ParseErrorTest.java @@ -154,6 +154,54 @@ public class ParseErrorTest { } } + @Test + public void unterminatedString() { + String input = "class T { String s = \"hello\nworld\"; }"; + try { + Parser.parse(input); + fail("expected parsing to fail"); + } catch (TurbineError e) { + assertThat(e.getMessage()) + .isEqualTo( + lines( + "<>:1: error: unterminated string literal", // + "class T { String s = \"hello", + " ^")); + } + } + + @Test + public void emptyChar() { + String input = "class T { char c = ''; }"; + try { + Parser.parse(input); + fail("expected parsing to fail"); + } catch (TurbineError e) { + assertThat(e.getMessage()) + .isEqualTo( + lines( + "<>:1: error: empty char literal", // + "class T { char c = ''; }", + " ^")); + } + } + + @Test + public void unterminatedChar() { + String input = "class T { char c = '; }"; + try { + Parser.parse(input); + fail("expected parsing to fail"); + } catch (TurbineError e) { + assertThat(e.getMessage()) + .isEqualTo( + lines( + "<>:1: error: unterminated char literal", // + "class T { char c = '; }", + " ^")); + } + } + private static String lines(String... lines) { return Joiner.on(System.lineSeparator()).join(lines); } @@ -15,15 +15,16 @@ </description> <properties> - <javac.version>9-dev-r3297-4</javac.version> - <asm.version>6.0</asm.version> + <asm.version>7.0</asm.version> + <javac.version>9+181-r4173-1</javac.version> + <guava.version>25.1-jre</guava.version> </properties> <dependencies> <dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> - <version>22.0</version> + <version>${guava.version}</version> </dependency> <dependency> <groupId>com.google.code.findbugs</groupId> @@ -64,15 +65,6 @@ <version>${javac.version}</version> <scope>test</scope> </dependency> - <!-- required for javap task --> - <dependency> - <groupId>com.sun</groupId> - <artifactId>tools</artifactId> - <scope>system</scope> - <optional>true</optional> - <version>1.8</version> - <systemPath>${java.home}/../lib/tools.jar</systemPath> - </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> @@ -82,7 +74,13 @@ <dependency> <groupId>com.google.truth</groupId> <artifactId>truth</artifactId> - <version>0.36</version> + <version>0.42</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.truth.extensions</groupId> + <artifactId>truth-java8-extension</artifactId> + <version>0.42</version> <scope>test</scope> </dependency> <dependency> @@ -91,6 +89,18 @@ <version>1.0</version> <scope>test</scope> </dependency> + <dependency> + <groupId>com.google.guava</groupId> + <artifactId>guava-testlib</artifactId> + <version>${guava.version}</version> + <scope>test</scope> + </dependency> + <dependency> + <groupId>com.google.auto.value</groupId> + <artifactId>auto-value</artifactId> + <version>1.5.3</version> + <scope>provided</scope> + </dependency> </dependencies> <build> @@ -101,6 +111,7 @@ <directory>javatests</directory> <includes> <include>**/testdata/**</include> + <include>**/moduletestdata/**</include> </includes> </testResource> </testResources> @@ -115,11 +126,14 @@ <plugin> <groupId>org.apache.maven.plugins</groupId> <artifactId>maven-compiler-plugin</artifactId> - <version>3.1</version> + <version>3.6.2</version> <configuration> + <fork>true</fork> <source>1.8</source> <target>1.8</target> <encoding>UTF-8</encoding> + <compilerArgument>-parameters</compilerArgument> + <testCompilerArgument>-parameters</testCompilerArgument> </configuration> </plugin> <plugin> @@ -147,10 +161,60 @@ <version>2.19.1</version> <configuration> <!-- set heap size to work around http://github.com/travis-ci/travis-ci/issues/3396 --> - <!-- put javac.jar on bootclasspath when executing tests --> - <argLine>-Xmx1024m -Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</argLine> + <argLine>-Xmx2g</argLine> </configuration> </plugin> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-shade-plugin</artifactId> + <version>2.4.3</version> + <executions> + <execution> + <id>shade-all-deps</id> + <phase>package</phase> + <goals> + <goal>shade</goal> + </goals> + <configuration> + <shadedArtifactAttached>true</shadedArtifactAttached> + <shadedClassifierName>all-deps</shadedClassifierName> + <createDependencyReducedPom>false</createDependencyReducedPom> + <!-- http://stackoverflow.com/a/6743609 --> + <filters> + <filter> + <artifact>*:*</artifact> + <excludes> + <exclude>META-INF/*.SF</exclude> + <exclude>META-INF/*.DSA</exclude> + <exclude>META-INF/*.RSA</exclude> + </excludes> + </filter> + </filters> + </configuration> + </execution> + </executions> + </plugin> + </plugins> </build> + <profiles> + <profile> + <id>java-8</id> + <activation> + <jdk>1.8</jdk> + </activation> + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + <configuration> + <!-- put javac.jar on bootclasspath when executing tests --> + <argLine>-Xbootclasspath/p:${settings.localRepository}/com/google/errorprone/javac/${javac.version}/javac-${javac.version}.jar</argLine> + </configuration> + </plugin> + </plugins> + </build> + </profile> + </profiles> </project> |