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