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