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