• 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.AnnotationValue;
22 import com.google.turbine.binder.bound.EnumConstantValue;
23 import com.google.turbine.binder.bound.ModuleInfo;
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       default:
85         throw new AssertionError(sig.boundKind());
86     }
87   }
88 
bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope)89   static Type bindTy(Sig.TySig sig, Function<String, TyVarSymbol> scope) {
90     switch (sig.kind()) {
91       case BASE_TY_SIG:
92         return Type.PrimTy.create(((Sig.BaseTySig) sig).type(), ImmutableList.of());
93       case CLASS_TY_SIG:
94         return bindClassTy((Sig.ClassTySig) sig, scope);
95       case TY_VAR_SIG:
96         return Type.TyVar.create(scope.apply(((Sig.TyVarSig) sig).name()), ImmutableList.of());
97       case ARRAY_TY_SIG:
98         return bindArrayTy((Sig.ArrayTySig) sig, scope);
99       case WILD_TY_SIG:
100         return wildTy((WildTySig) sig, scope);
101       case VOID_TY_SIG:
102         return Type.VOID;
103       default:
104         throw new AssertionError(sig.kind());
105     }
106   }
107 
bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope)108   private static Type bindArrayTy(Sig.ArrayTySig arrayTySig, Function<String, TyVarSymbol> scope) {
109     return Type.ArrayTy.create(bindTy(arrayTySig.elementType(), scope), ImmutableList.of());
110   }
111 
bindValue(Type type, ElementValue value)112   public static Const bindValue(Type type, ElementValue value) {
113     switch (value.kind()) {
114       case ENUM:
115         return bindEnumValue((EnumConstValue) value);
116       case CONST:
117         return bindConstValue(type, ((ConstValue) value).value());
118       case ARRAY:
119         return bindArrayValue(type, (ArrayValue) value);
120       case CLASS:
121         return new TurbineClassValue(
122             bindTy(
123                 new SigParser(((ConstTurbineClassValue) value).className()).parseType(),
124                 x -> {
125                   throw new IllegalStateException(x);
126                 }));
127       case ANNOTATION:
128         return bindAnnotationValue(type, ((ElementValue.AnnotationValue) value).annotation());
129     }
130     throw new AssertionError(value.kind());
131   }
132 
bindAnnotationValue(Type type, AnnotationInfo value)133   static AnnotationValue bindAnnotationValue(Type type, AnnotationInfo value) {
134     ClassSymbol sym = asClassSymbol(value.typeName());
135     ImmutableMap.Builder<String, Const> values = ImmutableMap.builder();
136     for (Map.Entry<String, ElementValue> e : value.elementValuePairs().entrySet()) {
137       values.put(e.getKey(), bindValue(type, e.getValue()));
138     }
139     return new AnnotationValue(sym, values.build());
140   }
141 
bindAnnotations(List<AnnotationInfo> input)142   static ImmutableList<AnnoInfo> bindAnnotations(List<AnnotationInfo> input) {
143     ImmutableList.Builder<AnnoInfo> result = ImmutableList.builder();
144     for (AnnotationInfo annotation : input) {
145       AnnotationValue anno = bindAnnotationValue(Type.VOID, annotation);
146       result.add(new AnnoInfo(null, anno.sym(), null, anno.values()));
147     }
148     return result.build();
149   }
150 
asClassSymbol(String s)151   private static ClassSymbol asClassSymbol(String s) {
152     return new ClassSymbol(s.substring(1, s.length() - 1));
153   }
154 
bindArrayValue(Type type, ArrayValue value)155   private static Const bindArrayValue(Type type, ArrayValue value) {
156     ImmutableList.Builder<Const> elements = ImmutableList.builder();
157     for (ElementValue element : value.elements()) {
158       elements.add(bindValue(type, element));
159     }
160     return new ArrayInitValue(elements.build());
161   }
162 
bindConstValue(Type type, Const.Value value)163   public static Const.Value bindConstValue(Type type, Const.Value value) {
164     if (type.tyKind() != Type.TyKind.PRIM_TY) {
165       return value;
166     }
167     // TODO(b/32626659): this is not bug-compatible with javac
168     switch (((Type.PrimTy) type).primkind()) {
169       case CHAR:
170         return new Const.CharValue(value.asChar().value());
171       case SHORT:
172         return new Const.ShortValue(value.asShort().value());
173       case INT:
174         return new Const.IntValue(value.asInteger().value());
175       case LONG:
176         return new Const.LongValue(value.asLong().value());
177       case FLOAT:
178         return new Const.FloatValue(value.asFloat().value());
179       case DOUBLE:
180         return new Const.DoubleValue(value.asDouble().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       case STRING:
187       case NULL:
188         return value;
189     }
190     throw new AssertionError(type);
191   }
192 
bindEnumValue(EnumConstValue value)193   private static Const bindEnumValue(EnumConstValue value) {
194     return new EnumConstantValue(
195         new FieldSymbol(asClassSymbol(value.typeName()), value.constName()));
196   }
197 
198   /**
199    * Returns a {@link ModuleInfo} given a module-info class file. Currently only the module's name,
200    * version, and flags are populated, since the directives are not needed by turbine at compile
201    * time.
202    */
bindModuleInfo(String path, Supplier<byte[]> bytes)203   public static ModuleInfo bindModuleInfo(String path, Supplier<byte[]> bytes) {
204     ClassFile classFile = ClassReader.read(path, bytes.get());
205     ClassFile.ModuleInfo module = classFile.module();
206     return new ModuleInfo(
207         module.name(),
208         module.version(),
209         module.flags(),
210         /* annos= */ ImmutableList.of(),
211         /* requires= */ ImmutableList.of(),
212         /* exports= */ ImmutableList.of(),
213         /* opens= */ ImmutableList.of(),
214         /* uses= */ ImmutableList.of(),
215         /* provides= */ ImmutableList.of());
216   }
217 }
218