| /* |
| * 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.binder; |
| |
| import static com.google.common.collect.Iterables.getLast; |
| import static java.util.Objects.requireNonNull; |
| |
| import com.google.common.base.Joiner; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.ImmutableMap; |
| import com.google.turbine.binder.bound.HeaderBoundClass; |
| import com.google.turbine.binder.bound.SourceHeaderBoundClass; |
| import com.google.turbine.binder.bound.SourceTypeBoundClass; |
| import com.google.turbine.binder.bound.TypeBoundClass.FieldInfo; |
| import com.google.turbine.binder.bound.TypeBoundClass.MethodInfo; |
| import com.google.turbine.binder.bound.TypeBoundClass.ParamInfo; |
| import com.google.turbine.binder.bound.TypeBoundClass.RecordComponentInfo; |
| import com.google.turbine.binder.bound.TypeBoundClass.TyVarInfo; |
| import com.google.turbine.binder.env.Env; |
| import com.google.turbine.binder.lookup.CompoundScope; |
| 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.binder.sym.MethodSymbol; |
| import com.google.turbine.binder.sym.ParamSymbol; |
| import com.google.turbine.binder.sym.RecordComponentSymbol; |
| import com.google.turbine.binder.sym.Symbol; |
| import com.google.turbine.binder.sym.TyVarSymbol; |
| 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 com.google.turbine.types.Deannotate; |
| import java.util.ArrayDeque; |
| import java.util.ArrayList; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import org.jspecify.nullness.Nullable; |
| |
| /** Type binding. */ |
| public class TypeBinder { |
| |
| /** A scope containing a single {@link Symbol}. */ |
| private static class SingletonScope implements Scope { |
| |
| private final String name; |
| private final Symbol sym; |
| |
| public SingletonScope(String name, Symbol sym) { |
| this.name = name; |
| this.sym = sym; |
| } |
| |
| @Override |
| public @Nullable LookupResult lookup(LookupKey lookup) { |
| if (name.equals(lookup.first().value())) { |
| return new LookupResult(sym, lookup); |
| } |
| return null; |
| } |
| } |
| |
| /** A scope backed by a map of simple names to {@link Symbol}s. */ |
| private static class MapScope implements Scope { |
| private final ImmutableMap<String, ? extends Symbol> tps; |
| |
| public MapScope(ImmutableMap<String, ? extends Symbol> tps) { |
| this.tps = tps; |
| } |
| |
| @Override |
| public @Nullable LookupResult lookup(LookupKey lookupKey) { |
| Symbol sym = tps.get(lookupKey.first().value()); |
| return sym != null ? new LookupResult(sym, lookupKey) : null; |
| } |
| } |
| |
| /** |
| * A scope containing all symbols in lexically enclosing scopes of a class, including type |
| * parameters, and declared and inherited members |
| */ |
| private static class ClassMemberScope implements Scope { |
| private final ClassSymbol sym; |
| private final Env<ClassSymbol, HeaderBoundClass> env; |
| |
| public ClassMemberScope(ClassSymbol sym, Env<ClassSymbol, HeaderBoundClass> env) { |
| this.sym = sym; |
| this.env = env; |
| } |
| |
| @Override |
| public @Nullable LookupResult lookup(LookupKey lookup) { |
| ClassSymbol curr = sym; |
| while (curr != null) { |
| Symbol result = Resolve.resolve(env, sym, curr, lookup.first()); |
| if (result != null) { |
| return new LookupResult(result, lookup); |
| } |
| HeaderBoundClass info = env.getNonNull(curr); |
| result = info.typeParameters().get(lookup.first().value()); |
| if (result != null) { |
| return new LookupResult(result, lookup); |
| } |
| curr = info.owner(); |
| } |
| return null; |
| } |
| } |
| |
| /** Creates {@link SourceTypeBoundClass} nodes for a compilation. */ |
| public static SourceTypeBoundClass 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( |
| TurbineLogWithSource log, |
| Env<ClassSymbol, HeaderBoundClass> env, |
| ClassSymbol owner, |
| SourceHeaderBoundClass base) { |
| this.log = log; |
| this.env = env; |
| this.owner = owner; |
| this.base = base; |
| } |
| |
| private SourceTypeBoundClass bind() { |
| // This method uses two scopes. This first one is built up as we process the signature |
| // and its elements become visible to subsequent elements (e.g. type parameters can |
| // refer to previous declared type parameters, the superclass type can refer to |
| // type parameters, ...). A second scope is created for finding methods and fields |
| // once the signature is fully determined. |
| CompoundScope enclosingScope = |
| base.scope() |
| .toScope(Resolve.resolveFunction(env, owner)) |
| .append(new SingletonScope(base.decl().name().value(), owner)); |
| if (base.owner() != null) { |
| enclosingScope = enclosingScope.append(new ClassMemberScope(base.owner(), env)); |
| } |
| |
| ImmutableList<AnnoInfo> annotations = bindAnnotations(enclosingScope, base.decl().annos()); |
| |
| CompoundScope bindingScope = enclosingScope; |
| |
| // type parameters can refer to each other in f-bounds, so update the scope first |
| bindingScope = bindingScope.append(new MapScope(base.typeParameters())); |
| final ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes = |
| bindTyParams(base.decl().typarams(), bindingScope, base.typeParameters()); |
| |
| ImmutableList.Builder<Type> interfaceTypes = ImmutableList.builder(); |
| Type superClassType; |
| switch (base.kind()) { |
| case ENUM: |
| superClassType = |
| Type.ClassTy.create( |
| ImmutableList.of( |
| Type.ClassTy.SimpleClassTy.create( |
| ClassSymbol.ENUM, |
| ImmutableList.of(Type.ClassTy.asNonParametricClassTy(owner)), |
| ImmutableList.of()))); |
| break; |
| case ANNOTATION: |
| superClassType = Type.ClassTy.OBJECT; |
| interfaceTypes.add(Type.ClassTy.asNonParametricClassTy(ClassSymbol.ANNOTATION)); |
| break; |
| case CLASS: |
| if (base.decl().xtnds().isPresent()) { |
| superClassType = bindClassTy(bindingScope, base.decl().xtnds().get()); |
| } else if (owner.equals(ClassSymbol.OBJECT)) { |
| // java.lang.Object doesn't have a superclass |
| superClassType = null; |
| } else { |
| superClassType = Type.ClassTy.OBJECT; |
| } |
| break; |
| case INTERFACE: |
| if (base.decl().xtnds().isPresent()) { |
| throw new AssertionError(); |
| } |
| superClassType = Type.ClassTy.OBJECT; |
| break; |
| case RECORD: |
| superClassType = Type.ClassTy.asNonParametricClassTy(ClassSymbol.RECORD); |
| break; |
| default: |
| throw new AssertionError(base.decl().tykind()); |
| } |
| |
| for (Tree.ClassTy i : base.decl().impls()) { |
| interfaceTypes.add(bindClassTy(bindingScope, i)); |
| } |
| |
| ImmutableList.Builder<ClassSymbol> permits = ImmutableList.builder(); |
| for (Tree.ClassTy i : base.decl().permits()) { |
| Type type = bindClassTy(bindingScope, i); |
| if (!type.tyKind().equals(Type.TyKind.CLASS_TY)) { |
| throw new AssertionError(type.tyKind()); |
| } |
| permits.add(((Type.ClassTy) type).sym()); |
| } |
| |
| CompoundScope scope = |
| base.scope() |
| .toScope(Resolve.resolveFunction(env, owner)) |
| .append(new SingletonScope(base.decl().name().value(), owner)) |
| .append(new ClassMemberScope(owner, env)); |
| |
| SyntheticMethods syntheticMethods = new SyntheticMethods(); |
| |
| ImmutableList<RecordComponentInfo> components = bindComponents(scope, base.decl().components()); |
| |
| ImmutableList.Builder<MethodInfo> methods = |
| ImmutableList.<MethodInfo>builder() |
| .addAll(syntheticMethods(syntheticMethods, components)) |
| .addAll(bindMethods(scope, base.decl().members(), components)); |
| if (base.kind().equals(TurbineTyKind.RECORD)) { |
| methods.addAll(syntheticRecordMethods(syntheticMethods, components)); |
| } |
| |
| ImmutableList<FieldInfo> fields = bindFields(scope, base.decl().members()); |
| |
| return new SourceTypeBoundClass( |
| interfaceTypes.build(), |
| permits.build(), |
| superClassType, |
| typeParameterTypes, |
| base.access(), |
| components, |
| methods.build(), |
| fields, |
| base.owner(), |
| base.kind(), |
| base.children(), |
| base.typeParameters(), |
| enclosingScope, |
| scope, |
| base.memberImports(), |
| /* annotation metadata */ null, |
| annotations, |
| base.source(), |
| base.decl()); |
| } |
| |
| /** |
| * A generated for synthetic {@link MethodSymbol}s. |
| * |
| * <p>Each {@link MethodSymbol} contains an index into its enclosing class, to enable comparing |
| * the symbols for equality. For synthetic methods we use an arbitrary unique negative index. |
| */ |
| private static class SyntheticMethods { |
| |
| private int idx = -1; |
| |
| MethodSymbol create(ClassSymbol owner, String name) { |
| return new MethodSymbol(idx--, owner, name); |
| } |
| } |
| |
| private ImmutableList<RecordComponentInfo> bindComponents( |
| CompoundScope scope, ImmutableList<Tree.VarDecl> components) { |
| ImmutableList.Builder<RecordComponentInfo> result = ImmutableList.builder(); |
| for (Tree.VarDecl p : components) { |
| int access = 0; |
| for (TurbineModifier m : p.mods()) { |
| access |= m.flag(); |
| } |
| RecordComponentInfo param = |
| new RecordComponentInfo( |
| new RecordComponentSymbol(owner, p.name().value()), |
| bindTy(scope, p.ty()), |
| bindAnnotations(scope, p.annos()), |
| access); |
| result.add(param); |
| } |
| return result.build(); |
| } |
| |
| /** Collect synthetic and implicit methods, including default constructors and enum methods. */ |
| ImmutableList<MethodInfo> syntheticMethods( |
| SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { |
| switch (base.kind()) { |
| case CLASS: |
| return maybeDefaultConstructor(syntheticMethods); |
| case RECORD: |
| return maybeDefaultRecordConstructor(syntheticMethods, components); |
| case ENUM: |
| return syntheticEnumMethods(syntheticMethods); |
| default: |
| return ImmutableList.of(); |
| } |
| } |
| |
| private ImmutableList<MethodInfo> maybeDefaultRecordConstructor( |
| SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { |
| if (hasConstructor()) { |
| return ImmutableList.of(); |
| } |
| MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); |
| ImmutableList.Builder<ParamInfo> params = ImmutableList.builder(); |
| for (RecordComponentInfo component : components) { |
| params.add( |
| new ParamInfo( |
| new ParamSymbol(symbol, component.name()), |
| component.type(), |
| component.annotations(), |
| component.access())); |
| } |
| return ImmutableList.of( |
| syntheticConstructor(symbol, params.build(), TurbineVisibility.fromAccess(base.access()))); |
| } |
| |
| private ImmutableList<MethodInfo> maybeDefaultConstructor(SyntheticMethods syntheticMethods) { |
| if (hasConstructor()) { |
| return ImmutableList.of(); |
| } |
| MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); |
| ImmutableList<ParamInfo> formals; |
| if (hasEnclosingInstance(base)) { |
| formals = ImmutableList.of(enclosingInstanceParameter(symbol)); |
| } else { |
| formals = ImmutableList.of(); |
| } |
| return ImmutableList.of( |
| syntheticConstructor(symbol, formals, TurbineVisibility.fromAccess(base.access()))); |
| } |
| |
| private MethodInfo syntheticConstructor( |
| MethodSymbol symbol, ImmutableList<ParamInfo> formals, TurbineVisibility visibility) { |
| int access = visibility.flag(); |
| access |= (base.access() & TurbineFlag.ACC_STRICT); |
| if (!formals.isEmpty() |
| && (getLast(formals).access() & TurbineFlag.ACC_VARARGS) == TurbineFlag.ACC_VARARGS) { |
| access |= TurbineFlag.ACC_VARARGS; |
| } |
| return new MethodInfo( |
| symbol, |
| ImmutableMap.of(), |
| Type.VOID, |
| formals, |
| ImmutableList.of(), |
| access | TurbineFlag.ACC_SYNTH_CTOR, |
| null, |
| null, |
| ImmutableList.of(), |
| null); |
| } |
| |
| private ParamInfo enclosingInstanceParameter(MethodSymbol owner) { |
| int access = TurbineFlag.ACC_FINAL; |
| if ((base.access() & TurbineFlag.ACC_PRIVATE) == TurbineFlag.ACC_PRIVATE) { |
| access |= TurbineFlag.ACC_SYNTHETIC; |
| } else { |
| access |= TurbineFlag.ACC_MANDATED; |
| } |
| int enclosingInstances = 0; |
| for (ClassSymbol sym = base.owner(); sym != null; ) { |
| HeaderBoundClass info = env.getNonNull(sym); |
| if (((info.access() & TurbineFlag.ACC_STATIC) == TurbineFlag.ACC_STATIC) |
| || info.owner() == null) { |
| break; |
| } |
| enclosingInstances++; |
| sym = info.owner(); |
| } |
| return new ParamInfo( |
| new ParamSymbol(owner, "this$" + enclosingInstances), |
| Type.ClassTy.asNonParametricClassTy(base.owner()), |
| ImmutableList.of(), |
| access); |
| } |
| |
| private static ImmutableList<ParamInfo> enumCtorParams(MethodSymbol owner) { |
| return ImmutableList.of( |
| new ParamInfo( |
| new ParamSymbol(owner, "$enum$name"), |
| Type.ClassTy.STRING, |
| ImmutableList.of(), |
| /*synthetic*/ |
| TurbineFlag.ACC_SYNTHETIC), |
| new ParamInfo( |
| new ParamSymbol(owner, "$enum$ordinal"), |
| Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), |
| ImmutableList.of(), |
| /*synthetic*/ |
| TurbineFlag.ACC_SYNTHETIC)); |
| } |
| |
| private ImmutableList<MethodInfo> syntheticEnumMethods(SyntheticMethods syntheticMethods) { |
| ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); |
| int access = 0; |
| access |= (base.access() & TurbineFlag.ACC_STRICT); |
| if (!hasConstructor()) { |
| MethodSymbol symbol = syntheticMethods.create(owner, "<init>"); |
| methods.add(syntheticConstructor(symbol, enumCtorParams(symbol), TurbineVisibility.PRIVATE)); |
| } |
| MethodSymbol valuesMethod = syntheticMethods.create(owner, "values"); |
| methods.add( |
| new MethodInfo( |
| valuesMethod, |
| ImmutableMap.of(), |
| Type.ArrayTy.create(Type.ClassTy.asNonParametricClassTy(owner), ImmutableList.of()), |
| ImmutableList.of(), |
| ImmutableList.of(), |
| access | TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC, |
| null, |
| null, |
| ImmutableList.of(), |
| null)); |
| MethodSymbol valueOfMethod = syntheticMethods.create(owner, "valueOf"); |
| methods.add( |
| new MethodInfo( |
| valueOfMethod, |
| ImmutableMap.of(), |
| Type.ClassTy.asNonParametricClassTy(owner), |
| ImmutableList.of( |
| new ParamInfo( |
| new ParamSymbol(valueOfMethod, "name"), |
| Type.ClassTy.STRING, |
| ImmutableList.of(), |
| TurbineFlag.ACC_MANDATED)), |
| ImmutableList.of(), |
| access | TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_STATIC, |
| null, |
| null, |
| ImmutableList.of(), |
| null)); |
| return methods.build(); |
| } |
| |
| private ImmutableList<MethodInfo> syntheticRecordMethods( |
| SyntheticMethods syntheticMethods, ImmutableList<RecordComponentInfo> components) { |
| ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder(); |
| MethodSymbol toStringMethod = syntheticMethods.create(owner, "toString"); |
| methods.add( |
| new MethodInfo( |
| toStringMethod, |
| ImmutableMap.of(), |
| Type.ClassTy.STRING, |
| ImmutableList.of(), |
| ImmutableList.of(), |
| TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, |
| null, |
| null, |
| ImmutableList.of(), |
| null)); |
| MethodSymbol hashCodeMethod = syntheticMethods.create(owner, "hashCode"); |
| methods.add( |
| new MethodInfo( |
| hashCodeMethod, |
| ImmutableMap.of(), |
| Type.PrimTy.create(TurbineConstantTypeKind.INT, ImmutableList.of()), |
| ImmutableList.of(), |
| ImmutableList.of(), |
| TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, |
| null, |
| null, |
| ImmutableList.of(), |
| null)); |
| MethodSymbol equalsMethod = syntheticMethods.create(owner, "equals"); |
| methods.add( |
| new MethodInfo( |
| equalsMethod, |
| ImmutableMap.of(), |
| Type.PrimTy.create(TurbineConstantTypeKind.BOOLEAN, ImmutableList.of()), |
| ImmutableList.of( |
| new ParamInfo( |
| new ParamSymbol(equalsMethod, "other"), |
| Type.ClassTy.OBJECT, |
| ImmutableList.of(), |
| TurbineFlag.ACC_MANDATED)), |
| ImmutableList.of(), |
| TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL, |
| null, |
| null, |
| ImmutableList.of(), |
| null)); |
| for (RecordComponentInfo c : components) { |
| MethodSymbol componentMethod = syntheticMethods.create(owner, c.name()); |
| methods.add( |
| new MethodInfo( |
| componentMethod, |
| ImmutableMap.of(), |
| c.type(), |
| ImmutableList.of(), |
| ImmutableList.of(), |
| TurbineFlag.ACC_PUBLIC, |
| null, |
| null, |
| c.annotations(), |
| null)); |
| } |
| return methods.build(); |
| } |
| |
| private boolean hasConstructor() { |
| for (Tree m : base.decl().members()) { |
| if (m.kind() != Kind.METH_DECL) { |
| continue; |
| } |
| if (((MethDecl) m).name().value().equals("<init>")) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** Bind type parameter types. */ |
| private ImmutableMap<TyVarSymbol, TyVarInfo> bindTyParams( |
| ImmutableList<Tree.TyParam> trees, CompoundScope scope, Map<String, TyVarSymbol> symbols) { |
| ImmutableMap.Builder<TyVarSymbol, TyVarInfo> result = ImmutableMap.builder(); |
| for (Tree.TyParam tree : trees) { |
| // `symbols` is constructed to guarantee the requireNonNull call is safe. |
| TyVarSymbol sym = requireNonNull(symbols.get(tree.name().value())); |
| ImmutableList.Builder<Type> bounds = ImmutableList.builder(); |
| for (Tree bound : tree.bounds()) { |
| bounds.add(bindTy(scope, bound)); |
| } |
| ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, tree.annos()); |
| result.put( |
| sym, |
| new TyVarInfo( |
| IntersectionTy.create(bounds.build()), /* lowerBound= */ null, annotations)); |
| } |
| return result.build(); |
| } |
| |
| private List<MethodInfo> bindMethods( |
| CompoundScope scope, |
| ImmutableList<Tree> members, |
| ImmutableList<RecordComponentInfo> components) { |
| List<MethodInfo> methods = new ArrayList<>(); |
| int idx = 0; |
| for (Tree member : members) { |
| if (member.kind() == Tree.Kind.METH_DECL) { |
| methods.add(bindMethod(idx++, scope, (MethDecl) member, components)); |
| } |
| } |
| return methods; |
| } |
| |
| private MethodInfo bindMethod( |
| int idx, CompoundScope scope, MethDecl t, ImmutableList<RecordComponentInfo> components) { |
| |
| MethodSymbol sym = new MethodSymbol(idx, 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().value(), new TyVarSymbol(sym, pt.name().value())); |
| } |
| typeParameters = builder.build(); |
| } |
| |
| // type parameters can refer to each other in f-bounds, so update the scope first |
| scope = scope.append(new MapScope(typeParameters)); |
| ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes = |
| bindTyParams(t.typarams(), scope, typeParameters); |
| |
| Type returnType; |
| if (t.ret().isPresent()) { |
| returnType = bindTy(scope, t.ret().get()); |
| } else { |
| returnType = Type.VOID; |
| } |
| |
| ImmutableList.Builder<ParamInfo> parameters = ImmutableList.builder(); |
| String name = t.name().value(); |
| if (name.equals("<init>")) { |
| if (hasEnclosingInstance(base)) { |
| parameters.add(enclosingInstanceParameter(sym)); |
| } else { |
| switch (base.kind()) { |
| case ENUM: |
| parameters.addAll(enumCtorParams(sym)); |
| break; |
| case RECORD: |
| if (t.mods().contains(TurbineModifier.COMPACT_CTOR)) { |
| for (RecordComponentInfo component : components) { |
| parameters.add( |
| new ParamInfo( |
| new ParamSymbol(sym, component.name()), |
| component.type(), |
| component.annotations(), |
| component.access())); |
| } |
| } |
| break; |
| default: |
| break; |
| } |
| } |
| } |
| ParamInfo receiver = null; |
| for (Tree.VarDecl p : t.params()) { |
| int access = 0; |
| for (TurbineModifier m : p.mods()) { |
| access |= m.flag(); |
| } |
| ParamInfo param = |
| new ParamInfo( |
| new ParamSymbol(sym, p.name().value()), |
| bindTy(scope, p.ty()), |
| bindAnnotations(scope, p.annos()), /*synthetic*/ |
| access); |
| if (p.name().value().equals("this")) { |
| receiver = param; |
| continue; |
| } |
| parameters.add(param); |
| } |
| ImmutableList.Builder<Type> exceptions = ImmutableList.builder(); |
| for (Tree.ClassTy p : t.exntys()) { |
| exceptions.add(bindClassTy(scope, p)); |
| } |
| |
| int access = 0; |
| for (TurbineModifier m : t.mods()) { |
| access |= m.flag(); |
| } |
| |
| switch (base.kind()) { |
| case INTERFACE: |
| case ANNOTATION: |
| // interface members have default public visibility |
| if ((access & TurbineVisibility.VISIBILITY_MASK) == 0) { |
| access |= TurbineFlag.ACC_PUBLIC; |
| } |
| if ((access |
| & (TurbineFlag.ACC_DEFAULT | TurbineFlag.ACC_STATIC | TurbineFlag.ACC_SYNTHETIC)) |
| == 0) { |
| access |= TurbineFlag.ACC_ABSTRACT; |
| } |
| if ((access & TurbineFlag.ACC_FINAL) == TurbineFlag.ACC_FINAL) { |
| log.error(t.position(), ErrorKind.UNEXPECTED_MODIFIER, TurbineModifier.FINAL); |
| } |
| break; |
| case ENUM: |
| if (name.equals("<init>")) { |
| access |= TurbineFlag.ACC_PRIVATE; |
| } |
| break; |
| default: |
| break; |
| } |
| |
| if (((base.access() & TurbineFlag.ACC_STRICT) == TurbineFlag.ACC_STRICT) |
| && (access & TurbineFlag.ACC_ABSTRACT) == 0) { |
| access |= TurbineFlag.ACC_STRICT; |
| } |
| |
| ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, t.annos()); |
| return new MethodInfo( |
| sym, |
| typeParameterTypes, |
| returnType, |
| parameters.build(), |
| exceptions.build(), |
| access, |
| null, |
| t, |
| annotations, |
| receiver); |
| } |
| |
| private static boolean hasEnclosingInstance(HeaderBoundClass base) { |
| return base.kind() == TurbineTyKind.CLASS |
| && base.owner() != null |
| && ((base.access() & TurbineFlag.ACC_STATIC) == 0); |
| } |
| |
| private ImmutableList<FieldInfo> bindFields(CompoundScope scope, ImmutableList<Tree> members) { |
| Set<FieldSymbol> seen = new HashSet<>(); |
| ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder(); |
| for (Tree member : members) { |
| if (member.kind() == Tree.Kind.VAR_DECL) { |
| FieldInfo field = bindField(scope, (Tree.VarDecl) member); |
| if (!seen.add(field.sym())) { |
| log.error(member.position(), ErrorKind.DUPLICATE_DECLARATION, "field: " + field.name()); |
| continue; |
| } |
| fields.add(field); |
| } |
| } |
| return fields.build(); |
| } |
| |
| private FieldInfo bindField(CompoundScope scope, Tree.VarDecl decl) { |
| FieldSymbol sym = new FieldSymbol(owner, decl.name().value()); |
| Type type = bindTy(scope, decl.ty()); |
| ImmutableList<AnnoInfo> annotations = bindAnnotations(scope, decl.annos()); |
| int access = 0; |
| for (TurbineModifier m : decl.mods()) { |
| access |= m.flag(); |
| } |
| switch (base.kind()) { |
| case INTERFACE: |
| case ANNOTATION: |
| access |= TurbineFlag.ACC_PUBLIC | TurbineFlag.ACC_FINAL | TurbineFlag.ACC_STATIC; |
| break; |
| default: |
| break; |
| } |
| return new FieldInfo(sym, type, access, annotations, decl, null); |
| } |
| |
| private ImmutableList<AnnoInfo> bindAnnotations( |
| CompoundScope scope, ImmutableList<Tree.Anno> trees) { |
| ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder(); |
| for (Tree.Anno tree : trees) { |
| 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, ImmutableMap.of())); |
| } |
| return result.build(); |
| } |
| |
| private @Nullable ClassSymbol resolveAnnoSymbol( |
| Anno tree, ImmutableList<Ident> name, @Nullable 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.getNonNull(sym).kind() != TurbineTyKind.ANNOTATION) { |
| log.error(tree.position(), ErrorKind.NOT_AN_ANNOTATION, sym); |
| } |
| return sym; |
| } |
| |
| private @Nullable ClassSymbol resolveNext(ClassSymbol sym, Ident bit) { |
| ClassSymbol next = Resolve.resolve(env, owner, sym, bit); |
| if (next == null) { |
| log.error( |
| bit.position(), |
| ErrorKind.SYMBOL_NOT_FOUND, |
| new ClassSymbol(sym.binaryName() + '$' + bit)); |
| } |
| return next; |
| } |
| |
| private ImmutableList<Type> bindTyArgs(CompoundScope scope, ImmutableList<Tree.Type> targs) { |
| ImmutableList.Builder<Type> result = ImmutableList.builder(); |
| for (Tree.Type ty : targs) { |
| result.add(bindTyArg(scope, ty)); |
| } |
| return result.build(); |
| } |
| |
| private Type bindTyArg(CompoundScope scope, Tree.Type ty) { |
| switch (ty.kind()) { |
| case WILD_TY: |
| return bindWildTy(scope, (Tree.WildTy) ty); |
| default: |
| Type result = bindTy(scope, ty); |
| if (result.tyKind().equals(Type.TyKind.PRIM_TY)) { |
| // Omit type annotations when printing the type in the diagnostic, since they're |
| // irrelevant and could be invalid if there were deferred errors. |
| // TODO(cushon): consider ensuring this is done for all diagnostics that mention types |
| log.error(ty.position(), ErrorKind.UNEXPECTED_TYPE, Deannotate.deannotate(result)); |
| } |
| return result; |
| } |
| } |
| |
| private Type bindTy(CompoundScope scope, Tree t) { |
| switch (t.kind()) { |
| case CLASS_TY: |
| return bindClassTy(scope, (Tree.ClassTy) t); |
| case PRIM_TY: |
| return bindPrimTy(scope, (Tree.PrimTy) t); |
| case ARR_TY: |
| return bindArrTy(scope, (Tree.ArrTy) t); |
| case VOID_TY: |
| return Type.VOID; |
| default: |
| throw new AssertionError(t.kind()); |
| } |
| } |
| |
| private Type bindClassTy(CompoundScope scope, Tree.ClassTy t) { |
| // flatten the components of a qualified class type |
| ArrayList<Tree.ClassTy> flat; |
| { |
| ArrayDeque<Tree.ClassTy> builder = new ArrayDeque<>(); |
| 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 |
| ImmutableList.Builder<Tree.Ident> nameBuilder = ImmutableList.builder(); |
| for (Tree.ClassTy curr : flat) { |
| 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) { |
| log.error(names.get(0).position(), ErrorKind.CANNOT_RESOLVE, Joiner.on('.').join(names)); |
| return Type.ErrorTy.create(names); |
| } |
| Symbol sym = result.sym(); |
| int annoIdx = flat.size() - result.remaining().size() - 1; |
| ImmutableList<AnnoInfo> annos = bindAnnotations(scope, flat.get(annoIdx).annos()); |
| switch (sym.symKind()) { |
| case CLASS: |
| // resolve any remaining types in the qualified name, and their type arguments |
| return bindClassTyRest(scope, flat, names, result, (ClassSymbol) sym, annos); |
| case TY_PARAM: |
| if (!result.remaining().isEmpty()) { |
| log.error(t.position(), ErrorKind.TYPE_PARAMETER_QUALIFIER); |
| return Type.ErrorTy.create(names); |
| } |
| return Type.TyVar.create((TyVarSymbol) sym, annos); |
| default: |
| throw new AssertionError(sym.symKind()); |
| } |
| } |
| |
| private Type bindClassTyRest( |
| CompoundScope scope, |
| ArrayList<ClassTy> flat, |
| 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( |
| Type.ClassTy.SimpleClassTy.create( |
| sym, bindTyArgs(scope, flat.get(idx++).tyargs()), annotations)); |
| for (; idx < flat.size(); idx++) { |
| Tree.ClassTy curr = flat.get(idx); |
| ClassSymbol next = resolveNext(sym, curr.name()); |
| if (next == null) { |
| return Type.ErrorTy.create(bits); |
| } |
| sym = next; |
| |
| annotations = bindAnnotations(scope, curr.annos()); |
| classes.add( |
| Type.ClassTy.SimpleClassTy.create(sym, bindTyArgs(scope, curr.tyargs()), annotations)); |
| } |
| return Type.ClassTy.create(classes.build()); |
| } |
| |
| private Type.PrimTy bindPrimTy(CompoundScope scope, PrimTy t) { |
| return Type.PrimTy.create(t.tykind(), bindAnnotations(scope, t.annos())); |
| } |
| |
| private Type bindArrTy(CompoundScope scope, Tree.ArrTy t) { |
| 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 Type.WildLowerBoundedTy.create(bindTy(scope, t.lower().get()), annotations); |
| } else if (t.upper().isPresent()) { |
| return Type.WildUpperBoundedTy.create(bindTy(scope, t.upper().get()), annotations); |
| } else { |
| return Type.WildUnboundedTy.create(annotations); |
| } |
| } |
| } |