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