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; 96 String name; 97 int idx = curr.sym().binaryName().lastIndexOf('/'); 98 if (idx == -1) { 99 pkg = ""; 100 name = curr.sym().binaryName(); 101 } else { 102 pkg = curr.sym().binaryName().substring(0, idx); 103 name = curr.sym().binaryName().substring(idx + 1); 104 } 105 classes.add(new Sig.SimpleClassTySig(name, tyArgSigs(curr))); 106 while (it.hasNext()) { 107 SimpleClassTy outer = curr; 108 curr = it.next(); 109 String shortname = curr.sym().binaryName().substring(outer.sym().binaryName().length() + 1); 110 classes.add(new Sig.SimpleClassTySig(shortname, tyArgSigs(curr))); 111 } 112 return new ClassTySig(pkg, classes.build()); 113 } 114 tyArgSigs(SimpleClassTy part)115 private ImmutableList<TySig> tyArgSigs(SimpleClassTy part) { 116 ImmutableList.Builder<TySig> tyargs = ImmutableList.builder(); 117 for (Type targ : part.targs()) { 118 tyargs.add(signature(targ)); 119 } 120 return tyargs.build(); 121 } 122 wildTy(WildTy ty)123 private TySig wildTy(WildTy ty) { 124 switch (ty.boundKind()) { 125 case NONE: 126 return new Sig.WildTyArgSig(); 127 case UPPER: 128 return new UpperBoundTySig(signature(((Type.WildUpperBoundedTy) ty).bound())); 129 case LOWER: 130 return new LowerBoundTySig(signature(((Type.WildLowerBoundedTy) ty).bound())); 131 default: 132 throw new AssertionError(ty.boundKind()); 133 } 134 } 135 136 /** 137 * Produces a method signature attribute for a generic method, or {@code null} if the signature is 138 * unnecessary. 139 */ methodSignature( Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo method, ClassSymbol sym)140 public String methodSignature( 141 Env<ClassSymbol, TypeBoundClass> env, 142 SourceTypeBoundClass.MethodInfo method, 143 ClassSymbol sym) { 144 if (!needsMethodSig(sym, env, method)) { 145 return null; 146 } 147 ImmutableList<Sig.TyParamSig> typarams = tyParamSig(method.tyParams(), env); 148 ImmutableList.Builder<Sig.TySig> fparams = ImmutableList.builder(); 149 for (SourceTypeBoundClass.ParamInfo t : method.parameters()) { 150 if (t.synthetic()) { 151 continue; 152 } 153 fparams.add(signature(t.type())); 154 } 155 Sig.TySig ret = signature(method.returnType()); 156 ImmutableList.Builder<Sig.TySig> excn = ImmutableList.builder(); 157 boolean needsExnSig = false; 158 for (Type e : method.exceptions()) { 159 if (needsSig(e)) { 160 needsExnSig = true; 161 break; 162 } 163 } 164 if (needsExnSig) { 165 for (Type e : method.exceptions()) { 166 excn.add(signature(e)); 167 } 168 } 169 MethodSig sig = new MethodSig(typarams, fparams.build(), ret, excn.build()); 170 return SigWriter.method(sig); 171 } 172 needsMethodSig( ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo m)173 private boolean needsMethodSig( 174 ClassSymbol sym, Env<ClassSymbol, TypeBoundClass> env, SourceTypeBoundClass.MethodInfo m) { 175 if ((env.get(sym).access() & TurbineFlag.ACC_ENUM) == TurbineFlag.ACC_ENUM 176 && m.name().equals("<init>")) { 177 // JDK-8024694: javac always expects signature attribute for enum constructors 178 return true; 179 } 180 if ((m.access() & TurbineFlag.ACC_SYNTH_CTOR) == TurbineFlag.ACC_SYNTH_CTOR) { 181 return false; 182 } 183 if (!m.tyParams().isEmpty()) { 184 return true; 185 } 186 if (m.returnType() != null && needsSig(m.returnType())) { 187 return true; 188 } 189 for (SourceTypeBoundClass.ParamInfo t : m.parameters()) { 190 if (t.synthetic()) { 191 continue; 192 } 193 if (needsSig(t.type())) { 194 return true; 195 } 196 } 197 for (Type t : m.exceptions()) { 198 if (needsSig(t)) { 199 return true; 200 } 201 } 202 return false; 203 } 204 205 /** 206 * Produces a class signature attribute for a generic class, or {@code null} if the signature is 207 * unnecessary. 208 */ classSignature(SourceTypeBoundClass info, Env<ClassSymbol, TypeBoundClass> env)209 public String classSignature(SourceTypeBoundClass info, Env<ClassSymbol, TypeBoundClass> env) { 210 if (!classNeedsSig(info)) { 211 return null; 212 } 213 ImmutableList<Sig.TyParamSig> typarams = tyParamSig(info.typeParameterTypes(), env); 214 215 ClassTySig xtnd = null; 216 if (info.superClassType() != null) { 217 xtnd = classTySig((ClassTy) info.superClassType()); 218 } 219 ImmutableList.Builder<ClassTySig> impl = ImmutableList.builder(); 220 for (Type i : info.interfaceTypes()) { 221 impl.add(classTySig((ClassTy) i)); 222 } 223 ClassSig sig = new ClassSig(typarams, xtnd, impl.build()); 224 return SigWriter.classSig(sig); 225 } 226 227 /** 228 * A field signature, or {@code null} if the descriptor provides all necessary type information. 229 */ fieldSignature(Type type)230 public String fieldSignature(Type type) { 231 return needsSig(type) ? SigWriter.type(signature(type)) : null; 232 } 233 classNeedsSig(SourceTypeBoundClass ci)234 private boolean classNeedsSig(SourceTypeBoundClass ci) { 235 if (!ci.typeParameters().isEmpty()) { 236 return true; 237 } 238 if (ci.superClassType() != null && needsSig(ci.superClassType())) { 239 return true; 240 } 241 for (Type i : ci.interfaceTypes()) { 242 if (needsSig(i)) { 243 return true; 244 } 245 } 246 return false; 247 } 248 needsSig(Type ty)249 private boolean needsSig(Type ty) { 250 switch (ty.tyKind()) { 251 case PRIM_TY: 252 case VOID_TY: 253 return false; 254 case CLASS_TY: 255 { 256 for (SimpleClassTy s : ((ClassTy) ty).classes()) { 257 if (!s.targs().isEmpty()) { 258 return true; 259 } 260 } 261 return false; 262 } 263 case ARRAY_TY: 264 return needsSig(((ArrayTy) ty).elementType()); 265 case TY_VAR: 266 return true; 267 default: 268 throw new AssertionError(ty.tyKind()); 269 } 270 } 271 tyParamSig( Map<TyVarSymbol, TyVarInfo> px, Env<ClassSymbol, TypeBoundClass> env)272 private ImmutableList<Sig.TyParamSig> tyParamSig( 273 Map<TyVarSymbol, TyVarInfo> px, Env<ClassSymbol, TypeBoundClass> env) { 274 ImmutableList.Builder<Sig.TyParamSig> result = ImmutableList.builder(); 275 for (Map.Entry<TyVarSymbol, SourceTypeBoundClass.TyVarInfo> entry : px.entrySet()) { 276 result.add(tyParamSig(entry.getKey(), entry.getValue(), env)); 277 } 278 return result.build(); 279 } 280 tyParamSig( TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env)281 private Sig.TyParamSig tyParamSig( 282 TyVarSymbol sym, SourceTypeBoundClass.TyVarInfo info, Env<ClassSymbol, TypeBoundClass> env) { 283 284 String identifier = sym.name(); 285 Sig.TySig cbound = null; 286 ImmutableList.Builder<Sig.TySig> ibounds = ImmutableList.builder(); 287 if (info.bound().bounds().isEmpty()) { 288 cbound = 289 new ClassTySig( 290 "java/lang", ImmutableList.of(new SimpleClassTySig("Object", ImmutableList.of()))); 291 } else { 292 boolean first = true; 293 for (Type bound : info.bound().bounds()) { 294 TySig sig = signature(bound); 295 if (first) { 296 if (!isInterface(bound, env)) { 297 cbound = sig; 298 continue; 299 } 300 } 301 ibounds.add(sig); 302 first = false; 303 } 304 } 305 return new Sig.TyParamSig(identifier, cbound, ibounds.build()); 306 } 307 isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env)308 private boolean isInterface(Type type, Env<ClassSymbol, TypeBoundClass> env) { 309 return type.tyKind() == TyKind.CLASS_TY 310 && env.get(((ClassTy) type).sym()).kind() == TurbineTyKind.INTERFACE; 311 } 312 descriptor(ClassSymbol sym)313 public String descriptor(ClassSymbol sym) { 314 classes.add(sym); 315 return sym.binaryName(); 316 } 317 objectType(ClassSymbol sym)318 String objectType(ClassSymbol sym) { 319 return "L" + descriptor(sym) + ";"; 320 } 321 } 322