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