Home | History | Annotate | Download | only in lower
      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.lower;
     18 
     19 import com.google.common.collect.ImmutableList;
     20 import com.google.turbine.binder.bound.SourceTypeBoundClass;
     21 import com.google.turbine.binder.bound.TypeBoundClass;
     22 import com.google.turbine.binder.env.Env;
     23 import com.google.turbine.binder.sym.ClassSymbol;
     24 import com.google.turbine.binder.sym.TyVarSymbol;
     25 import com.google.turbine.bytecode.sig.Sig;
     26 import com.google.turbine.bytecode.sig.Sig.ClassSig;
     27 import com.google.turbine.bytecode.sig.Sig.ClassTySig;
     28 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig;
     29 import com.google.turbine.bytecode.sig.Sig.MethodSig;
     30 import com.google.turbine.bytecode.sig.Sig.SimpleClassTySig;
     31 import com.google.turbine.bytecode.sig.Sig.TySig;
     32 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig;
     33 import com.google.turbine.bytecode.sig.SigWriter;
     34 import com.google.turbine.model.TurbineFlag;
     35 import com.google.turbine.type.Type;
     36 import com.google.turbine.type.Type.ArrayTy;
     37 import com.google.turbine.type.Type.ClassTy;
     38 import com.google.turbine.type.Type.ClassTy.SimpleClassTy;
     39 import com.google.turbine.type.Type.PrimTy;
     40 import com.google.turbine.type.Type.TyVar;
     41 import com.google.turbine.type.Type.WildTy;
     42 import java.util.Iterator;
     43 import java.util.LinkedHashSet;
     44 import java.util.Map;
     45 import java.util.Set;
     46 
     47 /** Translator from {@link Type}s to {@link Sig}natures. */
     48 public class LowerSignature {
     49 
     50   final Set<ClassSymbol> classes = new LinkedHashSet<>();
     51 
     52   /** Translates types to signatures. */
     53   public Sig.TySig signature(Type ty) {
     54     switch (ty.tyKind()) {
     55       case CLASS_TY:
     56         return classTySig((Type.ClassTy) ty);
     57       case TY_VAR:
     58         return tyVarSig((TyVar) ty);
     59       case ARRAY_TY:
     60         return arrayTySig((ArrayTy) ty);
     61       case PRIM_TY:
     62         return refBaseTy((PrimTy) ty);
     63       case VOID_TY:
     64         return Sig.VOID;
     65       case WILD_TY:
     66         return wildTy((WildTy) ty);
     67       default:
     68         throw new AssertionError(ty.tyKind());
     69     }
     70   }
     71 
     72   private Sig.BaseTySig refBaseTy(PrimTy t) {
     73     return new Sig.BaseTySig(t.primkind());
     74   }
     75 
     76   private Sig.ArrayTySig arrayTySig(ArrayTy t) {
     77     return new Sig.ArrayTySig(signature(t.elementType()));
     78   }
     79 
     80   private Sig.TyVarSig tyVarSig(TyVar t) {
     81     return new Sig.TyVarSig(t.sym().name());
     82   }
     83 
     84   private ClassTySig classTySig(ClassTy t) {
     85     classes.add(t.sym());
     86     ImmutableList.Builder<SimpleClassTySig> classes = ImmutableList.builder();
     87     Iterator<SimpleClassTy> it = t.classes.iterator();
     88     SimpleClassTy curr = it.next();
     89     while (curr.targs().isEmpty() && it.hasNext()) {
     90       curr = it.next();
     91     }
     92     String pkg;
     93     String name;
     94     int idx = curr.sym().binaryName().lastIndexOf('/');
     95     if (idx == -1) {
     96       pkg = "";
     97       name = curr.sym().binaryName();
     98     } else {
     99       pkg = curr.sym().binaryName().substring(0, idx);
    100       name = curr.sym().binaryName().substring(idx + 1);
    101     }
    102     classes.add(new Sig.SimpleClassTySig(name, tyArgSigs(curr)));
    103     while (it.hasNext()) {
    104       SimpleClassTy outer = curr;
    105       curr = it.next();
    106       String shortname = curr.sym().binaryName().substring(outer.sym().binaryName().length() + 1);
    107       classes.add(new Sig.SimpleClassTySig(shortname, tyArgSigs(curr)));
    108     }
    109     return new ClassTySig(pkg, classes.build());
    110   }
    111 
    112   private ImmutableList<TySig> tyArgSigs(SimpleClassTy part) {
    113     ImmutableList.Builder<TySig> tyargs = ImmutableList.builder();
    114     for (Type targ : part.targs()) {
    115       tyargs.add(signature(targ));
    116     }
    117     return tyargs.build();
    118   }
    119 
    120   private TySig wildTy(WildTy ty) {
    121     switch (ty.boundKind()) {
    122       case NONE:
    123         return new Sig.WildTyArgSig();
    124       case UPPER:
    125         return new UpperBoundTySig(signature(((Type.WildUpperBoundedTy) ty).bound()));
    126       case LOWER:
    127         return new LowerBoundTySig(signature(((Type.WildLowerBoundedTy) ty).bound()));
    128       default:
    129         throw new AssertionError(ty.boundKind());
    130     }
    131   }
    132 
    133   /**
    134    * Produces a method signature attribute for a generic method, or {@code null} if the signature is
    135    * unnecessary.
    136    */
    137   public String methodSignature(
    138       Env<ClassSymbol, TypeBoundClass> env,
    139       SourceTypeBoundClass.MethodInfo method,
    140       ClassSymbol sym) {
    141     if (!needsMethodSig(sym, env, method)) {
    142       return null;
    143     }
    144     ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams());
    145     ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder();
    146     for (SourceTypeBoundClass.ParamInfo t : method.parameters()) {
    147       if (t.synthetic()) {
    148         continue;
    149       }
    150       fparams.add(signature(t.type()));
    151     }
    152     Sig.TySig ret = signature(method.returnType());
    153     ImmutableList.Builder<Sig.TySig> excn = ImmutableList.builder();
    154     boolean needsExnSig = false;
    155     for (Type e : method.exceptions()) {
    156       if (needsSig(e)) {
    157         needsExnSig = true;
    158         break;
    159       }
    160     }
    161     if (needsExnSig) {
    162       for (Type e : method.exceptions()) {
    163         excn.add(signature(e));
    164       }
    165     }
    166     MethodSig sig = new MethodSig(typarams, fparams.build(), ret, excn.build());
    167     return SigWriter.method(sig);
    168   }
    169 
    170   private boolean needsMethodSig(
    171       ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo m) {
    172     if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM
    173         && m.name().equals("<init>")) {
    174       // JDK-8024694: javac always expects signature attribute for enum constructors
    175       return true;
    176     }
    177     if ((m.access() & TurbineFlag.ACC_SYNTH_CTOR) == TurbineFlag.ACC_SYNTH_CTOR) {
    178       return false;
    179     }
    180     if (!m.tyParams().isEmpty()) {
    181       return true;
    182     }
    183     if (m.returnType() != null && needsSig(m.returnType())) {
    184       return true;
    185     }
    186     for (SourceTypeBoundClass.ParamInfo t : m.parameters()) {
    187       if (t.synthetic()) {
    188         continue;
    189       }
    190       if (needsSig(t.type())) {
    191         return true;
    192       }
    193     }
    194     for (Type t : m.exceptions()) {
    195       if (needsSig(t)) {
    196         return true;
    197       }
    198     }
    199     return false;
    200   }
    201 
    202   /**
    203    * Produces a class signature attribute for a generic class, or {@code null} if the signature is
    204    * unnecessary.
    205    */
    206   public String classSignature(SourceTypeBoundClass info) {
    207     if (!classNeedsSig(info)) {
    208       return null;
    209     }
    210     ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes());
    211 
    212     ClassTySig xtnd = null;
    213     if (info.superClassType() != null) {
    214       xtnd = classTySig(info.superClassType());
    215     }
    216     ImmutableList.Builder<ClassTySig> impl = ImmutableList.builder();
    217     for (ClassTy i : info.interfaceTypes()) {
    218       impl.add(classTySig(i));
    219     }
    220     ClassSig sig = new ClassSig(typarams, xtnd, impl.build());
    221     return SigWriter.classSig(sig);
    222   }
    223 
    224   /**
    225    * A field signature, or {@code null} if the descriptor provides all necessary type information.
    226    */
    227   public String fieldSignature(Type type) {
    228     return needsSig(type) ? SigWriter.type(signature(type)) : null;
    229   }
    230 
    231   private boolean classNeedsSig(SourceTypeBoundClass ci) {
    232     if (!ci.typeParameters().isEmpty()) {
    233       return true;
    234     }
    235     if (ci.superClassType() != null && needsSig(ci.superClassType())) {
    236       return true;
    237     }
    238     for (ClassTy i : ci.interfaceTypes()) {
    239       if (needsSig(i)) {
    240         return true;
    241       }
    242     }
    243     return false;
    244   }
    245 
    246   private boolean needsSig(Type ty) {
    247     switch (ty.tyKind()) {
    248       case PRIM_TY:
    249       case VOID_TY:
    250         return false;
    251       case CLASS_TY:
    252         {
    253           for (SimpleClassTy s : ((ClassTy) ty).classes) {
    254             if (!s.targs().isEmpty()) {
    255               return true;
    256             }
    257           }
    258           return false;
    259         }
    260       case ARRAY_TY:
    261         return needsSig(((ArrayTy) ty).elementType());
    262       case TY_VAR:
    263         return true;
    264       default:
    265         throw new AssertionError(ty.tyKind());
    266     }
    267   }
    268 
    269   private ImmutableList<Sig.TyParamSig> tyParamSig(
    270       Map<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> px) {
    271     ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder();
    272     for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) {
    273       result.add(tyParamSig(entry.getKey(), entry.getValue()));
    274     }
    275     return result.build();
    276   }
    277 
    278   private Sig.TyParamSig tyParamSig(TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info) {
    279     String identifier = sym.name();
    280     Sig.TySig cbound = null;
    281     if (info.superClassBound() != null) {
    282       cbound = signature(info.superClassBound());
    283     } else if (info.interfaceBounds().isEmpty()) {
    284       cbound =
    285           new ClassTySig(
    286               "java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of())));
    287     }
    288     ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder();
    289     for (Type i : info.interfaceBounds()) {
    290       ibounds.add(signature(i));
    291     }
    292     return new Sig.TyParamSig(identifier, cbound, ibounds.build());
    293   }
    294 
    295   public String descriptor(ClassSymbol sym) {
    296     classes.add(sym);
    297     return sym.binaryName();
    298   }
    299 
    300   String objectType(ClassSymbol sym) {
    301     return "L" + descriptor(sym) + ";";
    302   }
    303 }
    304