Home | History | Annotate | Download | only in bytecode
      1 /*
      2  * Copyright 2016 Google Inc. All Rights Reserved.
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *     http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 package com.google.turbine.binder.bytecode;
     18 
     19 import static com.google.common.base.MoreObjects.firstNonNull;
     20 import static com.google.common.base.Verify.verify;
     21 
     22 import com.google.common.base.Supplier;
     23 import com.google.common.base.Suppliers;
     24 import com.google.common.collect.ImmutableList;
     25 import com.google.common.collect.ImmutableMap;
     26 import com.google.common.collect.ImmutableSet;
     27 import com.google.turbine.binder.bound.AnnotationMetadata;
     28 import com.google.turbine.binder.bound.BoundClass;
     29 import com.google.turbine.binder.bound.HeaderBoundClass;
     30 import com.google.turbine.binder.bound.TypeBoundClass;
     31 import com.google.turbine.binder.env.Env;
     32 import com.google.turbine.binder.sym.ClassSymbol;
     33 import com.google.turbine.binder.sym.FieldSymbol;
     34 import com.google.turbine.binder.sym.MethodSymbol;
     35 import com.google.turbine.binder.sym.TyVarSymbol;
     36 import com.google.turbine.bytecode.ClassFile;
     37 import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
     38 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
     39 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
     40 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
     41 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
     42 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.Kind;
     43 import com.google.turbine.bytecode.ClassFile.MethodInfo.ParameterInfo;
     44 import com.google.turbine.bytecode.ClassReader;
     45 import com.google.turbine.bytecode.sig.Sig;
     46 import com.google.turbine.bytecode.sig.Sig.ClassSig;
     47 import com.google.turbine.bytecode.sig.Sig.ClassTySig;
     48 import com.google.turbine.bytecode.sig.Sig.TySig;
     49 import com.google.turbine.bytecode.sig.SigParser;
     50 import com.google.turbine.model.Const;
     51 import com.google.turbine.model.TurbineElementType;
     52 import com.google.turbine.model.TurbineFlag;
     53 import com.google.turbine.model.TurbineTyKind;
     54 import com.google.turbine.type.AnnoInfo;
     55 import com.google.turbine.type.Type;
     56 import com.google.turbine.type.Type.ClassTy;
     57 import com.google.turbine.type.Type.IntersectionTy;
     58 import java.lang.annotation.RetentionPolicy;
     59 import java.util.Map;
     60 import java.util.function.Function;
     61 import javax.annotation.Nullable;
     62 
     63 /**
     64  * A bound class backed by a class file.
     65  *
     66  * <p>Implements all of the phase-specific bound class interfaces, and lazily fills in data from the
     67  * classfile needed to implement them. This is safe because the types in bytecode are already fully
     68  * resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work
     69  * done on the classpath.
     70  */
     71 public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass {
     72 
     73   private final ClassSymbol sym;
     74   private final Env<ClassSymbol, BytecodeBoundClass> env;
     75   private final Supplier<ClassFile> classFile;
     76   private final String jarFile;
     77 
     78   public BytecodeBoundClass(
     79       ClassSymbol sym,
     80       Supplier<byte[]> bytes,
     81       Env<ClassSymbol, BytecodeBoundClass> env,
     82       String jarFile) {
     83     this.sym = sym;
     84     this.env = env;
     85     this.jarFile = jarFile;
     86     this.classFile =
     87         Suppliers.memoize(
     88             new Supplier<ClassFile>() {
     89               @Override
     90               public ClassFile get() {
     91                 ClassFile cf = ClassReader.read(jarFile + "!" + sym.binaryName(), bytes.get());
     92                 verify(
     93                     cf.name().equals(sym.binaryName()),
     94                     "expected class data for %s, saw %s instead",
     95                     sym.binaryName(),
     96                     cf.name());
     97                 return cf;
     98               }
     99             });
    100   }
    101 
    102   private final Supplier<TurbineTyKind> kind =
    103       Suppliers.memoize(
    104           new Supplier<TurbineTyKind>() {
    105             @Override
    106             public TurbineTyKind get() {
    107               int access = access();
    108               if ((access & TurbineFlag.ACC_ANNOTATION) == TurbineFlag.ACC_ANNOTATION) {
    109                 return TurbineTyKind.ANNOTATION;
    110               }
    111               if ((access & TurbineFlag.ACC_INTERFACE) == TurbineFlag.ACC_INTERFACE) {
    112                 return TurbineTyKind.INTERFACE;
    113               }
    114               if ((access & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) {
    115                 return TurbineTyKind.ENUM;
    116               }
    117               return TurbineTyKind.CLASS;
    118             }
    119           });
    120 
    121   @Override
    122   public TurbineTyKind kind() {
    123     return kind.get();
    124   }
    125 
    126   private final Supplier<ClassSymbol> owner =
    127       Suppliers.memoize(
    128           new Supplier<ClassSymbol>() {
    129             @Override
    130             public ClassSymbol get() {
    131               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    132                 if (sym.binaryName().equals(inner.innerClass())) {
    133                   return new ClassSymbol(inner.outerClass());
    134                 }
    135               }
    136               return null;
    137             }
    138           });
    139 
    140   @Nullable
    141   @Override
    142   public ClassSymbol owner() {
    143     return owner.get();
    144   }
    145 
    146   private final Supplier<ImmutableMap<String, ClassSymbol>> children =
    147       Suppliers.memoize(
    148           new Supplier<ImmutableMap<String, ClassSymbol>>() {
    149             @Override
    150             public ImmutableMap<String, ClassSymbol> get() {
    151               ImmutableMap.Builder<String, ClassSymbol> result = ImmutableMap.builder();
    152               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    153                 if (inner.innerName() == null) {
    154                   // anonymous class
    155                   continue;
    156                 }
    157                 if (sym.binaryName().equals(inner.outerClass())) {
    158                   result.put(inner.innerName(), new ClassSymbol(inner.innerClass()));
    159                 }
    160               }
    161               return result.build();
    162             }
    163           });
    164 
    165   @Override
    166   public ImmutableMap<String, ClassSymbol> children() {
    167     return children.get();
    168   }
    169 
    170   private final Supplier<Integer> access =
    171       Suppliers.memoize(
    172           new Supplier<Integer>() {
    173             @Override
    174             public Integer get() {
    175               int access = classFile.get().access();
    176               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    177                 if (sym.binaryName().equals(inner.innerClass())) {
    178                   access = inner.access();
    179                 }
    180               }
    181               return access;
    182             }
    183           });
    184 
    185   @Override
    186   public int access() {
    187     return access.get();
    188   }
    189 
    190   private final Supplier<ClassSig> sig =
    191       Suppliers.memoize(
    192           new Supplier<ClassSig>() {
    193             @Override
    194             public ClassSig get() {
    195               String signature = classFile.get().signature();
    196               if (signature == null) {
    197                 return null;
    198               }
    199               return new SigParser(signature).parseClassSig();
    200             }
    201           });
    202 
    203   private final Supplier<ImmutableMap<String, TyVarSymbol>> tyParams =
    204       Suppliers.memoize(
    205           new Supplier<ImmutableMap<String, TyVarSymbol>>() {
    206             @Override
    207             public ImmutableMap<String, TyVarSymbol> get() {
    208               ClassSig csig = sig.get();
    209               if (csig == null || csig.tyParams().isEmpty()) {
    210                 return ImmutableMap.of();
    211               }
    212               ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder();
    213               for (Sig.TyParamSig p : csig.tyParams()) {
    214                 result.put(p.name(), new TyVarSymbol(sym, p.name()));
    215               }
    216               return result.build();
    217             }
    218           });
    219 
    220   @Override
    221   public ImmutableMap<String, TyVarSymbol> typeParameters() {
    222     return tyParams.get();
    223   }
    224 
    225   private final Supplier<ClassSymbol> superclass =
    226       Suppliers.memoize(
    227           new Supplier<ClassSymbol>() {
    228             @Override
    229             public ClassSymbol get() {
    230               String superclass = classFile.get().superName();
    231               if (superclass == null) {
    232                 return null;
    233               }
    234               return new ClassSymbol(superclass);
    235             }
    236           });
    237 
    238   @Override
    239   public ClassSymbol superclass() {
    240     return superclass.get();
    241   }
    242 
    243   private final Supplier<ImmutableList<ClassSymbol>> interfaces =
    244       Suppliers.memoize(
    245           new Supplier<ImmutableList<ClassSymbol>>() {
    246             @Override
    247             public ImmutableList<ClassSymbol> get() {
    248               ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder();
    249               for (String i : classFile.get().interfaces()) {
    250                 result.add(new ClassSymbol(i));
    251               }
    252               return result.build();
    253             }
    254           });
    255 
    256   @Override
    257   public ImmutableList<ClassSymbol> interfaces() {
    258     return interfaces.get();
    259   }
    260 
    261   private final Supplier<ClassTy> superClassType =
    262       Suppliers.memoize(
    263           new Supplier<ClassTy>() {
    264             @Override
    265             public ClassTy get() {
    266               if (superclass() == null) {
    267                 return null;
    268               }
    269               if (sig.get() == null || sig.get().superClass() == null) {
    270                 return ClassTy.asNonParametricClassTy(superclass());
    271               }
    272               return BytecodeBinder.bindClassTy(
    273                   sig.get().superClass(), makeScope(env, sym, ImmutableMap.of()));
    274             }
    275           });
    276 
    277   @Override
    278   public ClassTy superClassType() {
    279     return superClassType.get();
    280   }
    281 
    282   private final Supplier<ImmutableList<Type>> interfaceTypes =
    283       Suppliers.memoize(
    284           new Supplier<ImmutableList<Type>>() {
    285             @Override
    286             public ImmutableList<Type> get() {
    287               if (interfaces().isEmpty()) {
    288                 return ImmutableList.of();
    289               }
    290               ImmutableList.Builder<Type> result = ImmutableList.builder();
    291               if (sig.get() == null || sig.get().interfaces() == null) {
    292                 for (ClassSymbol sym : interfaces()) {
    293                   result.add(ClassTy.asNonParametricClassTy(sym));
    294                 }
    295               } else {
    296                 Function<String, TyVarSymbol> scope = makeScope(env, sym, ImmutableMap.of());
    297                 for (ClassTySig classTySig : sig.get().interfaces()) {
    298                   result.add(BytecodeBinder.bindClassTy(classTySig, scope));
    299                 }
    300               }
    301               return result.build();
    302             }
    303           });
    304 
    305   @Override
    306   public ImmutableList<Type> interfaceTypes() {
    307     return interfaceTypes.get();
    308   }
    309 
    310   private final Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes =
    311       Suppliers.memoize(
    312           new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() {
    313             @Override
    314             public ImmutableMap<TyVarSymbol, TyVarInfo> get() {
    315               if (sig.get() == null) {
    316                 return ImmutableMap.of();
    317               }
    318               ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder();
    319               Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters());
    320               for (Sig.TyParamSig p : sig.get().tyParams()) {
    321                 tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope));
    322               }
    323               return tparams.build();
    324             }
    325           });
    326 
    327   private static TyVarInfo bindTyParam(Sig.TyParamSig sig, Function<String, TyVarSymbol> scope) {
    328     ImmutableList.Builder<Type> bounds = ImmutableList.builder();
    329     if (sig.classBound() != null) {
    330       bounds.add(BytecodeBinder.bindTy(sig.classBound(), scope));
    331     }
    332     for (Sig.TySig t : sig.interfaceBounds()) {
    333       bounds.add(BytecodeBinder.bindTy(t, scope));
    334     }
    335     return new TyVarInfo(IntersectionTy.create(bounds.build()), ImmutableList.of());
    336   }
    337 
    338   @Override
    339   public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() {
    340     return typeParameterTypes.get();
    341   }
    342 
    343   private final Supplier<ImmutableList<FieldInfo>> fields =
    344       Suppliers.memoize(
    345           new Supplier<ImmutableList<FieldInfo>>() {
    346             @Override
    347             public ImmutableList<FieldInfo> get() {
    348               ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder();
    349               for (ClassFile.FieldInfo cfi : classFile.get().fields()) {
    350                 FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name());
    351                 Type type =
    352                     BytecodeBinder.bindTy(
    353                         new SigParser(cfi.descriptor()).parseType(),
    354                         makeScope(env, sym, ImmutableMap.of()));
    355                 int access = cfi.access();
    356                 Const.Value value = cfi.value();
    357                 if (value != null) {
    358                   value = BytecodeBinder.bindConstValue(type, value);
    359                 }
    360                 fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value));
    361               }
    362               return fields.build();
    363             }
    364           });
    365 
    366   @Override
    367   public ImmutableList<FieldInfo> fields() {
    368     return fields.get();
    369   }
    370 
    371   private final Supplier<ImmutableList<MethodInfo>> methods =
    372       Suppliers.memoize(
    373           new Supplier<ImmutableList<MethodInfo>>() {
    374             @Override
    375             public ImmutableList<MethodInfo> get() {
    376               ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
    377               for (ClassFile.MethodInfo m : classFile.get().methods()) {
    378                 methods.add(bindMethod(m));
    379               }
    380               return methods.build();
    381             }
    382           });
    383 
    384   private MethodInfo bindMethod(ClassFile.MethodInfo m) {
    385     MethodSymbol methodSymbol = new MethodSymbol(sym, m.name());
    386     Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig();
    387 
    388     ImmutableMap<String, TyVarSymbol> tyParams;
    389     {
    390       ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder();
    391       for (Sig.TyParamSig p : sig.tyParams()) {
    392         result.put(p.name(), new TyVarSymbol(methodSymbol, p.name()));
    393       }
    394       tyParams = result.build();
    395     }
    396 
    397     ImmutableMap<TyVarSymbol, TyVarInfo> tyParamTypes;
    398     {
    399       ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder();
    400       Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams);
    401       for (Sig.TyParamSig p : sig.tyParams()) {
    402         tparams.put(tyParams.get(p.name()), bindTyParam(p, scope));
    403       }
    404       tyParamTypes = tparams.build();
    405     }
    406 
    407     Function<String, TyVarSymbol> scope = makeScope(env, sym, tyParams);
    408 
    409     Type ret = null;
    410     if (sig.returnType() != null) {
    411       ret = BytecodeBinder.bindTy(sig.returnType(), scope);
    412     }
    413 
    414     ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder();
    415     int idx = 0;
    416     for (Sig.TySig tySig : sig.params()) {
    417       String name = null;
    418       int access = 0;
    419       if (idx < m.parameters().size()) {
    420         ParameterInfo paramInfo = m.parameters().get(idx);
    421         name = paramInfo.name();
    422         access = paramInfo.access();
    423       }
    424       ImmutableList<AnnoInfo> annotations =
    425           (idx < m.parameterAnnotations().size())
    426               ? BytecodeBinder.bindAnnotations(m.parameterAnnotations().get(idx))
    427               : ImmutableList.of();
    428       formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), name, annotations, access));
    429       idx++;
    430     }
    431 
    432     ImmutableList.Builder<Type> exceptions = ImmutableList.builder();
    433     for (TySig e : sig.exceptions()) {
    434       exceptions.add(BytecodeBinder.bindTy(e, scope));
    435     }
    436 
    437     Const defaultValue =
    438         m.defaultValue() != null ? BytecodeBinder.bindValue(ret, m.defaultValue()) : null;
    439 
    440     ImmutableList<AnnoInfo> annotations = BytecodeBinder.bindAnnotations(m.annotations());
    441 
    442     return new MethodInfo(
    443         methodSymbol,
    444         tyParamTypes,
    445         ret,
    446         formals.build(),
    447         exceptions.build(),
    448         m.access(),
    449         defaultValue,
    450         /* decl= */ null,
    451         annotations,
    452         /* receiver= */ null);
    453   }
    454 
    455   @Override
    456   public ImmutableList<MethodInfo> methods() {
    457     return methods.get();
    458   }
    459 
    460   private final Supplier<AnnotationMetadata> annotationMetadata =
    461       Suppliers.memoize(
    462           new Supplier<AnnotationMetadata>() {
    463             @Override
    464             public AnnotationMetadata get() {
    465               if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
    466                 return null;
    467               }
    468               RetentionPolicy retention = null;
    469               ImmutableSet<TurbineElementType> target = null;
    470               ClassSymbol repeatable = null;
    471               for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) {
    472                 switch (annotation.typeName()) {
    473                   case "Ljava/lang/annotation/Retention;":
    474                     retention = bindRetention(annotation);
    475                     break;
    476                   case "Ljava/lang/annotation/Target;":
    477                     target = bindTarget(annotation);
    478                     break;
    479                   case "Ljava/lang/annotation/Repeatable;":
    480                     repeatable = bindRepeatable(annotation);
    481                     break;
    482                   default:
    483                     break;
    484                 }
    485               }
    486               return new AnnotationMetadata(retention, target, repeatable);
    487             }
    488           });
    489 
    490   private RetentionPolicy bindRetention(AnnotationInfo annotation) {
    491     ElementValue val = annotation.elementValuePairs().get("value");
    492     if (val.kind() != Kind.ENUM) {
    493       return null;
    494     }
    495     EnumConstValue enumVal = (EnumConstValue) val;
    496     if (!enumVal.typeName().equals("Ljava/lang/annotation/RetentionPolicy;")) {
    497       return null;
    498     }
    499     return RetentionPolicy.valueOf(enumVal.constName());
    500   }
    501 
    502   private static ImmutableSet<TurbineElementType> bindTarget(AnnotationInfo annotation) {
    503     ImmutableSet.Builder<TurbineElementType> result = ImmutableSet.builder();
    504     ElementValue val = annotation.elementValuePairs().get("value");
    505     switch (val.kind()) {
    506       case ARRAY:
    507         for (ElementValue element : ((ArrayValue) val).elements()) {
    508           if (element.kind() == Kind.ENUM) {
    509             bindTargetElement(result, (EnumConstValue) element);
    510           }
    511         }
    512         break;
    513       case ENUM:
    514         bindTargetElement(result, (EnumConstValue) val);
    515         break;
    516       default:
    517         break;
    518     }
    519     return result.build();
    520   }
    521 
    522   private static void bindTargetElement(
    523       ImmutableSet.Builder<TurbineElementType> target, EnumConstValue enumVal) {
    524     if (enumVal.typeName().equals("Ljava/lang/annotation/ElementType;")) {
    525       target.add(TurbineElementType.valueOf(enumVal.constName()));
    526     }
    527   }
    528 
    529   private static ClassSymbol bindRepeatable(AnnotationInfo annotation) {
    530     ElementValue val = annotation.elementValuePairs().get("value");
    531     switch (val.kind()) {
    532       case CLASS:
    533         String className = ((ConstTurbineClassValue) val).className();
    534         return new ClassSymbol(className.substring(1, className.length() - 1));
    535       default:
    536         break;
    537     }
    538     return null;
    539   }
    540 
    541   @Override
    542   public AnnotationMetadata annotationMetadata() {
    543     return annotationMetadata.get();
    544   }
    545 
    546   private final Supplier<ImmutableList<AnnoInfo>> annotations =
    547       Suppliers.memoize(
    548           new Supplier<ImmutableList<AnnoInfo>>() {
    549             @Override
    550             public ImmutableList<AnnoInfo> get() {
    551               return BytecodeBinder.bindAnnotations(classFile.get().annotations());
    552             }
    553           });
    554 
    555   @Override
    556   public ImmutableList<AnnoInfo> annotations() {
    557     return annotations.get();
    558   }
    559 
    560   /**
    561    * Create a scope for resolving type variable symbols declared in the class, and any enclosing
    562    * instances.
    563    */
    564   private static Function<String, TyVarSymbol> makeScope(
    565       final Env<ClassSymbol, BytecodeBoundClass> env,
    566       final ClassSymbol sym,
    567       final Map<String, TyVarSymbol> typeVariables) {
    568     return new Function<String, TyVarSymbol>() {
    569       @Override
    570       public TyVarSymbol apply(String input) {
    571         TyVarSymbol result = typeVariables.get(input);
    572         if (result != null) {
    573           return result;
    574         }
    575         ClassSymbol curr = sym;
    576         while (curr != null) {
    577           BytecodeBoundClass info = env.get(curr);
    578           if (info == null) {
    579             throw new AssertionError(curr);
    580           }
    581           result = info.typeParameters().get(input);
    582           if (result != null) {
    583             return result;
    584           }
    585           curr = info.owner();
    586         }
    587         throw new AssertionError(input);
    588       }
    589     };
    590   }
    591 
    592   /** The jar file the symbol was loaded from. */
    593   public String jarFile() {
    594     return jarFile;
    595   }
    596 
    597   /** The class file the symbol was loaded from. */
    598   public ClassFile classFile() {
    599     return classFile.get();
    600   }
    601 }
    602