• 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.binder.bytecode;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.common.collect.ImmutableMap;
21 import com.google.turbine.binder.bound.EnumConstantValue;
22 import com.google.turbine.binder.bound.ModuleInfo;
23 import com.google.turbine.binder.bound.TurbineAnnotationValue;
24 import com.google.turbine.binder.bound.TurbineClassValue;
25 import com.google.turbine.binder.sym.ClassSymbol;
26 import com.google.turbine.binder.sym.FieldSymbol;
27 import com.google.turbine.binder.sym.TyVarSymbol;
28 import com.google.turbine.bytecode.ClassFile;
29 import com.google.turbine.bytecode.ClassFile.AnnotationInfo;
30 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue;
31 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ArrayValue;
32 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstTurbineClassValue;
33 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.ConstValue;
34 import com.google.turbine.bytecode.ClassFile.AnnotationInfo.ElementValue.EnumConstValue;
35 import com.google.turbine.bytecode.ClassReader;
36 import com.google.turbine.bytecode.sig.Sig;
37 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig;
38 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig;
39 import com.google.turbine.bytecode.sig.Sig.WildTySig;
40 import com.google.turbine.bytecode.sig.SigParser;
41 import com.google.turbine.model.Const;
42 import com.google.turbine.model.Const.ArrayInitValue;
43 import com.google.turbine.type.AnnoInfo;
44 import com.google.turbine.type.Type;
45 import java.util.ArrayList;
46 import java.util.List;
47 import java.util.Map;
48 import java.util.function.Function;
49 import java.util.function.Supplier;
50 
51 /** Bind {@link Type}s from bytecode. */
52 public class BytecodeBinder {
53 
bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope)54   static Type.ClassTy bindClassTy(Sig.ClassTySig sig, Function<String, TyVarSymbol> scope) {
55     StringBuilder sb = new StringBuilder(sig.pkg());
56     boolean first = true;
57     List<Type.ClassTy.SimpleClassTy> classes = new ArrayList<>();
58     for (Sig.SimpleClassTySig s : sig.classes()) {
59       sb.append(first ? '/' : '$');
60       sb.append(s.simpleName());
61       ClassSymbol sym = new ClassSymbol(sb.toString());
62 
63       ImmutableList.Builder<Type> tyArgs = ImmutableList.builder();
64       for (Sig.TySig arg : s.tyArgs()) {
65         tyArgs.add(bindTy(arg, scope));
66       }
67 
68       classes.add(Type.ClassTy.SimpleClassTy.create(sym, tyArgs.build(), ImmutableList.of()));
69       first = false;
70     }
71     return Type.ClassTy.create(classes);
72   }
73 
wildTy(WildTySig sig, Function<String, TyVarSymbol> scope)74   private static Type wildTy(WildTySig sig, Function<String, TyVarSymbol> scope) {
75     switch (sig.boundKind()) {
76       case NONE:
77         return Type.WildUnboundedTy.create(ImmutableList.of());
78       case LOWER:
79         return Type.WildLowerBoundedTy.create(
80             bindTy(((LowerBoundTySig) sig).bound(), scope), ImmutableList.of());
81       case UPPER:
82         return Type.WildUpperBoundedTy.create(
83             bindTy(((UpperBoundTySig) sig).bound(), scope), ImmutableList.of());
84     }
85     throw new AssertionError(sig.boundKind());
86   }
87 
bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope)88   static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) {
89     switch (sig.kind()) {
90       case BASE_TY_SIG:
91         return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of());
92       case CLASS_TY_SIG:
93         return bindClassTy((Sig.ClassTySig) sig, scope);
94       case TY_VAR_SIG:
95         return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of());
96       case ARRAY_TY_SIG:
97         return bindArrayTy((Sig.ArrayTySig) sig, scope);
98       case WILD_TY_SIG:
99         return wildTy((WildTySig) sig, scope);
100       case VOID_TY_SIG:
101         return Type.VOID;
102     }
103     throw new AssertionError(sig.kind());
104   }
105 
bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope)106   private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) {
107     return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of());
108   }
109 
bindValue(ElementValue value)110   public static Const bindValue(ElementValue value) {
111     switch (value.kind()) {
112       case ENUM:
113         return bindEnumValue((EnumConstValue) value);
114       case CONST:
115         return ((ConstValue) value).value();
116       case ARRAY:
117         return bindArrayValue((ArrayValue) value);
118       case CLASS:
119         return new TurbineClassValue(
120             bindTy(
121                 new SigParser(((ConstTurbineClassValue) value).className()).parseType(),
122                 x -> {
123                   throw new IllegalStateException(x);
124                 }));
125       case ANNOTATION:
126         return bindAnnotationValue(((ElementValue.ConstTurbineAnnotationValue) value).annotation());
127     }
128     throw new AssertionError(value.kind());
129   }
130 
bindAnnotationValue(AnnotationInfo value)131   static TurbineAnnotationValue bindAnnotationValue(AnnotationInfo value) {
132     ClassSymbol sym = asClassSymbol(value.typeName());
133     ImmutableMap.Builder<String, Const> values = ImmutableMap.builder();
134     for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) {
135       values.put(e.getKey(), bindValue(e.getValue()));
136     }
137     return new TurbineAnnotationValue(new AnnoInfo(null, sym, null, values.build()));
138   }
139 
bindAnnotations(List<AnnotationInfo> input)140   static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) {
141     ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
142     for (AnnotationInfo annotation : input) {
143       TurbineAnnotationValue anno = bindAnnotationValue(annotation);
144       if (!shouldSkip(anno)) {
145         result.add(anno.info());
146       }
147     }
148     return result.build();
149   }
150 
shouldSkip(TurbineAnnotationValue anno)151   private static boolean shouldSkip(TurbineAnnotationValue anno) {
152     // ct.sym contains fake annotations without corresponding class files.
153     return anno.sym().equals(ClassSymbol.PROFILE_ANNOTATION)
154         || anno.sym().equals(ClassSymbol.PROPRIETARY_ANNOTATION);
155   }
156 
asClassSymbol(String s)157   private static ClassSymbol asClassSymbol(String s) {
158     return new ClassSymbol(s.substring(1, s.length() - 1));
159   }
160 
bindArrayValue(ArrayValue value)161   private static Const bindArrayValue(ArrayValue value) {
162     ImmutableList.Builder<Const> elements = ImmutableList.builder();
163     for (ElementValue element : value.elements()) {
164       elements.add(bindValue(element));
165     }
166     return new ArrayInitValue(elements.build());
167   }
168 
bindConstValue(Type type, Const.Value value)169   public static Const.Value bindConstValue(Type type, Const.Value value) {
170     if (type.tyKind() != Type.TyKind.PRIM_TY) {
171       return value;
172     }
173     // Deficient numberic types and booleans are all stored as ints in the class file,
174     // coerce them to the target type.
175     // TODO(b/32626659): this is not bug-compatible with javac
176     switch (((Type.PrimTy) type).primkind()) {
177       case CHAR:
178         return new Const.CharValue(value.asChar().value());
179       case SHORT:
180         return new Const.ShortValue(value.asShort().value());
181       case BOOLEAN:
182         // boolean constants are encoded as integers
183         return new Const.BooleanValue(value.asInteger().value() != 0);
184       case BYTE:
185         return new Const.ByteValue(value.asByte().value());
186       default:
187         return value;
188     }
189   }
190 
bindEnumValue(EnumConstValue value)191   private static Const bindEnumValue(EnumConstValue value) {
192     return new EnumConstantValue(
193         new FieldSymbol(asClassSymbol(value.typeName()), value.constName()));
194   }
195 
196   /**
197    * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name,
198    * version, and flags are populated, since the directives are not needed by turbine at compile
199    * time.
200    */
bindModuleInfo(String path, Supplier<byte[]> bytes)201   public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) {
202     ClassFile classFile = ClassReader.read(path, bytes.get());
203     ClassFile.ModuleInfo module = classFile.module();
204     return new ModuleInfo(
205         module.name(),
206         module.version(),
207         module.flags(),
208         /* annos= */ ImmutableList.of(),
209         /* requires= */ ImmutableList.of(),
210         /* exports= */ ImmutableList.of(),
211         /* opens= */ ImmutableList.of(),
212         /* uses= */ ImmutableList.of(),
213         /* provides= */ ImmutableList.of());
214   }
215 }
216