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