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