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.ConstClassValue;
     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.ClassReader;
     44 import com.google.turbine.bytecode.sig.Sig;
     45 import com.google.turbine.bytecode.sig.Sig.ClassSig;
     46 import com.google.turbine.bytecode.sig.SigParser;
     47 import com.google.turbine.model.Const;
     48 import com.google.turbine.model.TurbineFlag;
     49 import com.google.turbine.model.TurbineTyKind;
     50 import com.google.turbine.type.Type;
     51 import com.google.turbine.type.Type.ClassTy;
     52 import java.lang.annotation.ElementType;
     53 import java.lang.annotation.RetentionPolicy;
     54 import java.util.Map;
     55 import java.util.function.Function;
     56 import javax.annotation.Nullable;
     57 
     58 /**
     59  * A bound class backed by a class file.
     60  *
     61  * <p>Implements all of the phase-specific bound class interfaces, and lazily fills in data from the
     62  * classfile needed to implement them. This is safe because the types in bytecode are already fully
     63  * resolved and canonicalized so there are no cycles. The laziness also minimizes the amount of work
     64  * done on the classpath.
     65  */
     66 public class BytecodeBoundClass implements BoundClass, HeaderBoundClass, TypeBoundClass {
     67 
     68   private final ClassSymbol sym;
     69   private final Env<ClassSymbol, BytecodeBoundClass> env;
     70   private final Supplier<ClassFile> classFile;
     71   private final String jarFile;
     72 
     73   public BytecodeBoundClass(
     74       ClassSymbol sym,
     75       final Supplier<byte[]> bytes,
     76       Env<ClassSymbol, BytecodeBoundClass> env,
     77       String jarFile) {
     78     this.sym = sym;
     79     this.env = env;
     80     this.jarFile = jarFile;
     81     this.classFile =
     82         Suppliers.memoize(
     83             new Supplier<ClassFile>() {
     84               @Override
     85               public ClassFile get() {
     86                 ClassFile cf = ClassReader.read(jarFile + "!" + sym, bytes.get());
     87                 verify(
     88                     cf.name().equals(sym.binaryName()),
     89                     "expected class data for %s, saw %s instead",
     90                     sym.binaryName(),
     91                     cf.name());
     92                 return cf;
     93               }
     94             });
     95   }
     96 
     97   final Supplier<TurbineTyKind> kind =
     98       Suppliers.memoize(
     99           new Supplier<TurbineTyKind>() {
    100             @Override
    101             public TurbineTyKind get() {
    102               int access = access();
    103               if ((access & TurbineFlag.ACC_ANNOTATION) == TurbineFlag.ACC_ANNOTATION) {
    104                 return TurbineTyKind.ANNOTATION;
    105               }
    106               if ((access & TurbineFlag.ACC_INTERFACE) == TurbineFlag.ACC_INTERFACE) {
    107                 return TurbineTyKind.INTERFACE;
    108               }
    109               if ((access & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM) {
    110                 return TurbineTyKind.ENUM;
    111               }
    112               return TurbineTyKind.CLASS;
    113             }
    114           });
    115 
    116   @Override
    117   public TurbineTyKind kind() {
    118     return kind.get();
    119   }
    120 
    121   final Supplier<ClassSymbol> owner =
    122       Suppliers.memoize(
    123           new Supplier<ClassSymbol>() {
    124             @Override
    125             public ClassSymbol get() {
    126               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    127                 if (sym.binaryName().equals(inner.innerClass())) {
    128                   return new ClassSymbol(inner.outerClass());
    129                 }
    130               }
    131               return null;
    132             }
    133           });
    134 
    135   @Nullable
    136   @Override
    137   public ClassSymbol owner() {
    138     return owner.get();
    139   }
    140 
    141   final Supplier<ImmutableMap<String, ClassSymbol>> children =
    142       Suppliers.memoize(
    143           new Supplier<ImmutableMap<String, ClassSymbol>>() {
    144             @Override
    145             public ImmutableMap<String, ClassSymbol> get() {
    146               ImmutableMap.Builder<String, ClassSymbol> result = ImmutableMap.builder();
    147               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    148                 if (inner.innerName() == null) {
    149                   // anonymous class
    150                   continue;
    151                 }
    152                 if (sym.binaryName().equals(inner.outerClass())) {
    153                   result.put(inner.innerName(), new ClassSymbol(inner.innerClass()));
    154                 }
    155               }
    156               return result.build();
    157             }
    158           });
    159 
    160   @Override
    161   public ImmutableMap<String, ClassSymbol> children() {
    162     return children.get();
    163   }
    164 
    165   final Supplier<Integer> access =
    166       Suppliers.memoize(
    167           new Supplier<Integer>() {
    168             @Override
    169             public Integer get() {
    170               int access = classFile.get().access();
    171               for (ClassFile.InnerClass inner : classFile.get().innerClasses()) {
    172                 if (sym.binaryName().equals(inner.innerClass())) {
    173                   access = inner.access();
    174                 }
    175               }
    176               return access;
    177             }
    178           });
    179 
    180   @Override
    181   public int access() {
    182     return access.get();
    183   }
    184 
    185   private final Supplier<ClassSig> sig =
    186       Suppliers.memoize(
    187           new Supplier<ClassSig>() {
    188             @Override
    189             public ClassSig get() {
    190               String signature = classFile.get().signature();
    191               if (signature == null) {
    192                 return null;
    193               }
    194               return new SigParser(signature).parseClassSig();
    195             }
    196           });
    197 
    198   private final Supplier<ImmutableMap<String, TyVarSymbol>> tyParams =
    199       Suppliers.memoize(
    200           new Supplier<ImmutableMap<String, TyVarSymbol>>() {
    201             @Override
    202             public ImmutableMap<String, TyVarSymbol> get() {
    203               ClassSig csig = sig.get();
    204               if (csig == null || csig.tyParams().isEmpty()) {
    205                 return ImmutableMap.of();
    206               }
    207               ImmutableMap.Builder<String, TyVarSymbol> result = ImmutableMap.builder();
    208               for (Sig.TyParamSig p : csig.tyParams()) {
    209                 result.put(p.name(), new TyVarSymbol(sym, p.name()));
    210               }
    211               return result.build();
    212             }
    213           });
    214 
    215   @Override
    216   public ImmutableMap<String, TyVarSymbol> typeParameters() {
    217     return tyParams.get();
    218   }
    219 
    220   final Supplier<ClassSymbol> superclass =
    221       Suppliers.memoize(
    222           new Supplier<ClassSymbol>() {
    223             @Override
    224             public ClassSymbol get() {
    225               String superclass = classFile.get().superName();
    226               if (superclass == null) {
    227                 return null;
    228               }
    229               return new ClassSymbol(superclass);
    230             }
    231           });
    232 
    233   @Override
    234   public ClassSymbol superclass() {
    235     return superclass.get();
    236   }
    237 
    238   final Supplier<ImmutableList<ClassSymbol>> interfaces =
    239       Suppliers.memoize(
    240           new Supplier<ImmutableList<ClassSymbol>>() {
    241             @Override
    242             public ImmutableList<ClassSymbol> get() {
    243               ImmutableList.Builder<ClassSymbol> result = ImmutableList.builder();
    244               for (String i : classFile.get().interfaces()) {
    245                 result.add(new ClassSymbol(i));
    246               }
    247               return result.build();
    248             }
    249           });
    250 
    251   @Override
    252   public ImmutableList<ClassSymbol> interfaces() {
    253     return interfaces.get();
    254   }
    255 
    256   Supplier<ClassTy> superClassType =
    257       Suppliers.memoize(
    258           new Supplier<ClassTy>() {
    259             @Override
    260             public ClassTy get() {
    261               if (superclass() == null) {
    262                 return null;
    263               }
    264               if (sig.get() == null || sig.get().superClass() == null) {
    265                 return ClassTy.asNonParametricClassTy(superclass());
    266               }
    267               return BytecodeBinder.bindClassTy(
    268                   sig.get().superClass(), makeScope(env, sym, ImmutableMap.of()));
    269             }
    270           });
    271 
    272   @Override
    273   public ClassTy superClassType() {
    274     return superClassType.get();
    275   }
    276 
    277   Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>> typeParameterTypes =
    278       Suppliers.memoize(
    279           new Supplier<ImmutableMap<TyVarSymbol, TyVarInfo>>() {
    280             @Override
    281             public ImmutableMap<TyVarSymbol, TyVarInfo> get() {
    282               if (sig.get() == null) {
    283                 return ImmutableMap.of();
    284               }
    285               ImmutableMap.Builder<TyVarSymbol, TyVarInfo> tparams = ImmutableMap.builder();
    286               Function<String, TyVarSymbol> scope = makeScope(env, sym, typeParameters());
    287               for (Sig.TyParamSig p : sig.get().tyParams()) {
    288                 tparams.put(typeParameters().get(p.name()), bindTyParam(p, scope));
    289               }
    290               return tparams.build();
    291             }
    292           });
    293 
    294   private static TyVarInfo bindTyParam(Sig.TyParamSig sig, Function<String, TyVarSymbol> scope) {
    295     Type superClassBound = null;
    296     if (sig.classBound() != null) {
    297       superClassBound = BytecodeBinder.bindTy(sig.classBound(), scope);
    298     }
    299     ImmutableList.Builder<Type> interfaceBounds = ImmutableList.builder();
    300     for (Sig.TySig t : sig.interfaceBounds()) {
    301       interfaceBounds.add(BytecodeBinder.bindTy(t, scope));
    302     }
    303     return new TyVarInfo(superClassBound, interfaceBounds.build(), ImmutableList.of());
    304   }
    305 
    306   @Override
    307   public ImmutableMap<TyVarSymbol, TyVarInfo> typeParameterTypes() {
    308     return typeParameterTypes.get();
    309   }
    310 
    311   private final Supplier<ImmutableList<FieldInfo>> fields =
    312       Suppliers.memoize(
    313           new Supplier<ImmutableList<FieldInfo>>() {
    314             @Override
    315             public ImmutableList<FieldInfo> get() {
    316               ImmutableList.Builder<FieldInfo> fields = ImmutableList.builder();
    317               for (ClassFile.FieldInfo cfi : classFile.get().fields()) {
    318                 FieldSymbol fieldSym = new FieldSymbol(sym, cfi.name());
    319                 // we don't need a type here, the const value has all necessary information
    320                 Type type = null;
    321                 int access = cfi.access();
    322                 Const.Value value = cfi.value();
    323                 if (value != null) {
    324                   // TODO(b/32626659): this is not bug-compatible with javac
    325                   switch (cfi.descriptor()) {
    326                     case "Z":
    327                       // boolean constants are encoded as integers
    328                       value = new Const.BooleanValue(value.asInteger().value() != 0);
    329                       break;
    330                     case "C":
    331                       value = new Const.CharValue(value.asChar().value());
    332                       break;
    333                     case "S":
    334                       value = new Const.ShortValue(value.asShort().value());
    335                       break;
    336                     case "B":
    337                       value = new Const.ByteValue(value.asByte().value());
    338                       break;
    339                     default:
    340                       break;
    341                   }
    342                 }
    343                 fields.add(new FieldInfo(fieldSym, type, access, ImmutableList.of(), null, value));
    344               }
    345               return fields.build();
    346             }
    347           });
    348 
    349   @Override
    350   public ImmutableList<FieldInfo> fields() {
    351     return fields.get();
    352   }
    353 
    354   Supplier<ImmutableList<MethodInfo>> methods =
    355       Suppliers.memoize(
    356           new Supplier<ImmutableList<MethodInfo>>() {
    357             @Override
    358             public ImmutableList<MethodInfo> get() {
    359               // we only need methods for annotation declarations
    360               if (kind() != TurbineTyKind.ANNOTATION) {
    361                 return ImmutableList.of();
    362               }
    363               ImmutableList.Builder<MethodInfo> methods = ImmutableList.builder();
    364               for (ClassFile.MethodInfo m : classFile.get().methods()) {
    365                 methods.add(bindMethod(m));
    366               }
    367               return methods.build();
    368             }
    369           });
    370 
    371   private MethodInfo bindMethod(ClassFile.MethodInfo m) {
    372     MethodSymbol methodSymbol = new MethodSymbol(sym, m.name());
    373     Sig.MethodSig sig = new SigParser(firstNonNull(m.signature(), m.descriptor())).parseMethodSig();
    374 
    375     verify(sig.tyParams().isEmpty());
    376 
    377     Function<String, TyVarSymbol> scope = makeScope(env, sym, ImmutableMap.of());
    378 
    379     Type ret = null;
    380     if (sig.returnType() != null) {
    381       ret = BytecodeBinder.bindTy(sig.returnType(), scope);
    382     }
    383 
    384     ImmutableList.Builder<ParamInfo> formals = ImmutableList.builder();
    385     for (Sig.TySig tySig : sig.params()) {
    386       formals.add(new ParamInfo(BytecodeBinder.bindTy(tySig, scope), null, ImmutableList.of(), 0));
    387     }
    388 
    389     verify(sig.exceptions().isEmpty());
    390 
    391     return new MethodInfo(
    392         methodSymbol,
    393         ImmutableMap.of(),
    394         ret,
    395         formals.build(),
    396         ImmutableList.of(),
    397         m.access(),
    398         null /*defaultValue*/,
    399         null /*decl*/,
    400         ImmutableList.of(),
    401         null);
    402   }
    403 
    404   @Override
    405   public ImmutableList<MethodInfo> methods() {
    406     return methods.get();
    407   }
    408 
    409   final Supplier<AnnotationMetadata> annotationMetadata =
    410       Suppliers.memoize(
    411           new Supplier<AnnotationMetadata>() {
    412             @Override
    413             public AnnotationMetadata get() {
    414               if ((access() & TurbineFlag.ACC_ANNOTATION) != TurbineFlag.ACC_ANNOTATION) {
    415                 return null;
    416               }
    417               RetentionPolicy retention = null;
    418               ImmutableSet<ElementType> target = null;
    419               ClassSymbol repeatable = null;
    420               for (ClassFile.AnnotationInfo annotation : classFile.get().annotations()) {
    421                 switch (annotation.typeName()) {
    422                   case "Ljava/lang/annotation/Retention;":
    423                     retention = bindRetention(annotation);
    424                     break;
    425                   case "Ljava/lang/annotation/Target;":
    426                     target = bindTarget(annotation);
    427                     break;
    428                   case "Ljava/lang/annotation/Repeatable;":
    429                     repeatable = bindRepeatable(annotation);
    430                     break;
    431                   default:
    432                     break;
    433                 }
    434               }
    435               return new AnnotationMetadata(retention, target, repeatable);
    436             }
    437           });
    438 
    439   private RetentionPolicy bindRetention(AnnotationInfo annotation) {
    440     ElementValue val = annotation.elementValuePairs().get("value");
    441     if (val.kind() != Kind.ENUM) {
    442       return null;
    443     }
    444     EnumConstValue enumVal = (EnumConstValue) val;
    445     if (!enumVal.typeName().equals("Ljava/lang/annotation/RetentionPolicy;")) {
    446       return null;
    447     }
    448     return RetentionPolicy.valueOf(enumVal.constName());
    449   }
    450 
    451   private static ImmutableSet<ElementType> bindTarget(AnnotationInfo annotation) {
    452     ImmutableSet.Builder<ElementType> result = ImmutableSet.builder();
    453     ElementValue val = annotation.elementValuePairs().get("value");
    454     switch (val.kind()) {
    455       case ARRAY:
    456         for (ElementValue element : ((ArrayValue) val).elements()) {
    457           if (element.kind() == Kind.ENUM) {
    458             bindTargetElement(result, (EnumConstValue) element);
    459           }
    460         }
    461         break;
    462       case ENUM:
    463         bindTargetElement(result, (EnumConstValue) val);
    464         break;
    465       default:
    466         break;
    467     }
    468     return result.build();
    469   }
    470 
    471   private static void bindTargetElement(
    472       ImmutableSet.Builder<ElementType> target, EnumConstValue enumVal) {
    473     if (enumVal.typeName().equals("Ljava/lang/annotation/ElementType;")) {
    474       target.add(ElementType.valueOf(enumVal.constName()));
    475     }
    476   }
    477 
    478   private static ClassSymbol bindRepeatable(AnnotationInfo annotation) {
    479     ElementValue val = annotation.elementValuePairs().get("value");
    480     switch (val.kind()) {
    481       case CLASS:
    482         String className = ((ConstClassValue) val).className();
    483         return new ClassSymbol(className.substring(1, className.length() - 1));
    484       default:
    485         break;
    486     }
    487     return null;
    488   }
    489 
    490   @Override
    491   public AnnotationMetadata annotationMetadata() {
    492     return annotationMetadata.get();
    493   }
    494 
    495   /**
    496    * Create a scope for resolving type variable symbols declared in the class, and any enclosing
    497    * instances.
    498    */
    499   private static Function<String, TyVarSymbol> makeScope(
    500       final Env<ClassSymbol, BytecodeBoundClass> env,
    501       final ClassSymbol sym,
    502       final Map<String, TyVarSymbol> typeVariables) {
    503     return new Function<String, TyVarSymbol>() {
    504       @Override
    505       public TyVarSymbol apply(String input) {
    506         TyVarSymbol result = typeVariables.get(input);
    507         if (result != null) {
    508           return result;
    509         }
    510         ClassSymbol curr = sym;
    511         while (curr != null) {
    512           BytecodeBoundClass info = env.get(curr);
    513           if (info == null) {
    514             throw new AssertionError(curr);
    515           }
    516           result = info.typeParameters().get(input);
    517           if (result != null) {
    518             return result;
    519           }
    520           curr = info.owner();
    521         }
    522         throw new AssertionError(input);
    523       }
    524     };
    525   }
    526 
    527   /** The jar file the symbol was loaded from. */
    528   public String jarFile() {
    529     return jarFile;
    530   }
    531 
    532   /** The class file the symbol was loaded from. */
    533   public ClassFile classFile() {
    534     return classFile.get();
    535   }
    536 }
    537