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.bytecode.sig;
18 
19 import com.google.common.collect.ImmutableList;
20 import com.google.turbine.bytecode.sig.Sig.ArrayTySig;
21 import com.google.turbine.bytecode.sig.Sig.BaseTySig;
22 import com.google.turbine.bytecode.sig.Sig.ClassSig;
23 import com.google.turbine.bytecode.sig.Sig.ClassTySig;
24 import com.google.turbine.bytecode.sig.Sig.LowerBoundTySig;
25 import com.google.turbine.bytecode.sig.Sig.MethodSig;
26 import com.google.turbine.bytecode.sig.Sig.SimpleClassTySig;
27 import com.google.turbine.bytecode.sig.Sig.TyParamSig;
28 import com.google.turbine.bytecode.sig.Sig.TySig;
29 import com.google.turbine.bytecode.sig.Sig.TyVarSig;
30 import com.google.turbine.bytecode.sig.Sig.UpperBoundTySig;
31 import com.google.turbine.bytecode.sig.Sig.WildTyArgSig;
32 import com.google.turbine.model.TurbineConstantTypeKind;
33 
34 /** Parser for JVMS 4.3.4 signatures. */
35 public class SigParser {
36 
37   /** The string to parse. */
38   private final String sig;
39 
40   /** The current position. */
41   private int idx = 0;
42 
43   /** Returns the next character to process, without advancing. */
peek()44   char peek() {
45     return sig.charAt(idx);
46   }
47 
48   /** Returns the next character and advances. */
eat()49   char eat() {
50     return sig.charAt(idx++);
51   }
52 
53   /** Returns true if there is more input to process. */
hasNext()54   boolean hasNext() {
55     return idx < sig.length();
56   }
57 
SigParser(String sig)58   public SigParser(String sig) {
59     this.sig = sig;
60   }
61 
parseFieldSig()62   public TySig parseFieldSig() {
63     switch (peek()) {
64       case '[':
65         return parseArraySig();
66       case 'T':
67         return parseTyVar();
68       case 'L':
69         return parseClassTySig();
70       case '+':
71         eat();
72         return new UpperBoundTySig(parseFieldSig());
73       case '-':
74         eat();
75         return new LowerBoundTySig(parseFieldSig());
76       case '*':
77         eat();
78         return new WildTyArgSig();
79       default:
80         throw new AssertionError(peek());
81     }
82   }
83 
84   /** Parses a MethodTypeSignature into a {@link MethodSig}. */
parseMethodSig()85   public MethodSig parseMethodSig() {
86     ImmutableList<TyParamSig> tyParams = parseTyParams();
87     if (peek() != '(') {
88       throw new AssertionError();
89     }
90     eat();
91     ImmutableList.Builder<TySig> params = ImmutableList.builder();
92     while (peek() != ')') {
93       params.add(parseType());
94     }
95     eat();
96     ImmutableList.Builder<TySig> exceptions = ImmutableList.builder();
97     TySig result = parseType();
98     while (hasNext() && eat() == '^') {
99       exceptions.add(parseFieldSig());
100     }
101     return new MethodSig(tyParams, params.build(), result, exceptions.build());
102   }
103 
104   /** Parses a ClassTypeSignature into a {@link ClassSig}. */
parseClassSig()105   public ClassSig parseClassSig() {
106     ClassTySig superClass;
107     ImmutableList<TyParamSig> tyParams = parseTyParams();
108     superClass = parseClassTySig();
109     ImmutableList.Builder<ClassTySig> interfaces = ImmutableList.builder();
110     while (hasNext()) {
111       interfaces.add(parseClassTySig());
112     }
113     return new ClassSig(tyParams, superClass, interfaces.build());
114   }
115 
parseTyParams()116   private ImmutableList<TyParamSig> parseTyParams() {
117     ImmutableList.Builder<TyParamSig> tyParams = ImmutableList.builder();
118     if (peek() == '<') {
119       eat();
120       do {
121         StringBuilder identifier = new StringBuilder();
122         char ch;
123         while ((ch = eat()) != ':') {
124           identifier.append(ch);
125         }
126         TySig classBound = null;
127         switch (peek()) {
128           case 'L':
129           case '[':
130           case 'T':
131             classBound = parseFieldSig();
132             break;
133           default:
134             break;
135         }
136         ImmutableList.Builder<TySig> interfaceBounds = ImmutableList.builder();
137         while (peek() == ':') {
138           eat();
139           interfaceBounds.add(parseFieldSig());
140         }
141         tyParams.add(new TyParamSig(identifier.toString(), classBound, interfaceBounds.build()));
142       } while (peek() != '>');
143       eat();
144     }
145     return tyParams.build();
146   }
147 
148   /** Parses a type signature. */
parseType()149   public TySig parseType() {
150     switch (peek()) {
151       case 'Z':
152         eat();
153         return new BaseTySig(TurbineConstantTypeKind.BOOLEAN);
154       case 'C':
155         eat();
156         return new BaseTySig(TurbineConstantTypeKind.CHAR);
157       case 'B':
158         eat();
159         return new BaseTySig(TurbineConstantTypeKind.BYTE);
160       case 'S':
161         eat();
162         return new BaseTySig(TurbineConstantTypeKind.SHORT);
163       case 'I':
164         eat();
165         return new BaseTySig(TurbineConstantTypeKind.INT);
166       case 'F':
167         eat();
168         return new BaseTySig(TurbineConstantTypeKind.FLOAT);
169       case 'J':
170         eat();
171         return new BaseTySig(TurbineConstantTypeKind.LONG);
172       case 'D':
173         eat();
174         return new BaseTySig(TurbineConstantTypeKind.DOUBLE);
175       case 'V':
176         eat();
177         return Sig.VOID;
178       default:
179         return parseFieldSig();
180     }
181   }
182 
parseArraySig()183   private ArrayTySig parseArraySig() {
184     eat();
185     TySig elementType = parseType();
186     return new ArrayTySig(elementType);
187   }
188 
parseTyVar()189   private TyVarSig parseTyVar() {
190     eat();
191     StringBuilder name = new StringBuilder();
192     char ch;
193     while ((ch = eat()) != ';') {
194       name.append(ch);
195     }
196     return new TyVarSig(name.toString());
197   }
198 
parseClassTySig()199   private ClassTySig parseClassTySig() {
200     eat();
201     ImmutableList.Builder<SimpleClassTySig> simples = ImmutableList.builder();
202     StringBuilder name = new StringBuilder();
203     StringBuilder pkg = new StringBuilder();
204     ImmutableList.Builder<TySig> tyArgs = ImmutableList.builder();
205     OUTER:
206     while (true) {
207       switch (peek()) {
208         case '/':
209           eat();
210           if (pkg.length() > 0) {
211             pkg.append('/');
212           }
213           pkg.append(name);
214           name = new StringBuilder();
215           break;
216         case '<':
217           {
218             eat();
219             do {
220               tyArgs.add(parseFieldSig());
221             } while (peek() != '>');
222             eat();
223             break;
224           }
225         case '.':
226           {
227             eat();
228             simples.add(new SimpleClassTySig(name.toString(), tyArgs.build()));
229             tyArgs = ImmutableList.builder();
230             name = new StringBuilder();
231             break;
232           }
233         case ';':
234           break OUTER;
235         default:
236           name.append(eat());
237           break;
238       }
239     }
240     simples.add(new SimpleClassTySig(name.toString(), tyArgs.build()));
241     eat();
242     return new ClassTySig(pkg.toString(), simples.build());
243   }
244 }
245