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