001 /* Copyright 2000, 2001, Compaq Computer Corporation */
002
003 /* =========================================================================
004 * DescriptorParser.java
005 * ========================================================================= */
006
007 package javafe.reader;
008
009 import java.util.*;
010
011 import javafe.ast.*;
012 import javafe.util.*;
013
014 /* -------------------------------------------------------------------------
015 * DescriptorParser
016 * ------------------------------------------------------------------------- */
017
018 /**
019 * Parses various kinds of class-file-format descriptor strings into
020 * abstract syntax.
021 */
022
023 class DescriptorParser
024 {
025 /* -- package class methods ---------------------------------------------- */
026
027 /**
028 * Return a type name for a given class-file class-name string.
029 * @param s the class name to parse
030 * @return the type name encoded by s
031 */
032 //@ ensures \result != null;
033 static TypeName parseClass(/*@ non_null @*/ String s)
034 throws ClassFormatError
035 {
036 // tokenize the string into a sequence of identifiers delimited by slashes
037 // check syntax of identifiers? ???
038
039 StringTokenizer tokenizer = new StringTokenizer(s, "/$");
040
041 int count = tokenizer.countTokens();
042 javafe.util.Assert.notFalse(count>0); //@ nowarn Pre;
043
044 Identifier[] identifiers = new Identifier[count];
045 int[] locations1 = new int[count];
046 int[] locations2 = new int[count-1];
047
048 for (int i = 0; tokenizer.hasMoreTokens(); i++)
049 {
050 identifiers[i] = Identifier.intern(tokenizer.nextToken());
051 //@ assert identifiers[i] != null;
052 locations1[i] = classLocation;
053
054 if (i<count-1)
055 locations2[i] = classLocation;
056 }
057 //@ assume \nonnullelements(identifiers);
058 /*@ assume (\forall int i; (0<=i && i<locations1.length)
059 ==> locations1[i] != Location.NULL); */
060 /*@ assume (\forall int i; (0<=i && i<locations2.length)
061 ==> locations2[i] != Location.NULL); */
062
063 return TypeName.make(Name.make(locations1, locations2,
064 IdentifierVec.make(identifiers)));
065 }
066
067 /**
068 * Return a type for a given class-file field descriptor string.
069 * @param s the field descriptor to parse
070 * @return the type encoded by s
071 */
072 //@ requires s != null;
073 //@ ensures \result != null;
074 //@ ensures \result.syntax;
075 static Type parseField(String s)
076 throws ClassFormatError
077 {
078 // parse the descriptor as a type and make sure it's only a type
079
080 StringScanner scanner = new StringScanner(s);
081 Type type = parseType(scanner);
082
083 if (scanner.index<s.length())
084 throw new ClassFormatError("junk after field descriptor");
085
086 return type;
087 }
088
089 /**
090 * Return a method signature for a given class-file method descriptor string.
091 * @param s the method descriptor to parse
092 * @return the method signature encoded by s
093 */
094 //@ requires s != null;
095 //@ ensures \result != null;
096 static MethodSignature parseMethod(String s)
097 throws ClassFormatError
098 {
099 // check the format of the method descriptor and construct a string scanner
100
101 int length = s.length();
102
103 if (length<3)
104 throw new ClassFormatError("incomplete method descriptor");
105
106 if (s.charAt(0) != '(')
107 throw new ClassFormatError("invalid method descriptor");
108
109 StringScanner scanner = new StringScanner(s);
110
111 scanner.index++;
112
113 // parse the parameter types in the method descriptor and accumulate them
114 // in a method signature
115
116 MethodSignature signature = new MethodSignature(classLocation);
117
118 while (s.charAt(scanner.index) != ')')
119 {
120 signature.appendParameter(parseType(scanner));
121
122 if (scanner.index>=length)
123 throw new ClassFormatError("incomplete method descriptor");
124 }
125
126 scanner.index++;
127
128 // parse the return type of the method descriptor and store it in the
129 // method signature
130
131 signature.setReturn(parseReturn(scanner));
132
133 if (scanner.index<length)
134 throw new ClassFormatError("junk after method descriptor");
135
136 return signature;
137 }
138
139 /* -- package class variables -------------------------------------------- */
140
141 /**
142 * A dummy location representing the class being parsed.
143 * Should be set externally.
144 */
145 //@ invariant classLocation != Location.NULL;
146 static int classLocation = Location.createFakeLoc("[unknown]");
147
148 /* -- private class methods ---------------------------------------------- */
149
150 /**
151 * Parse a type from a given class-file field descriptor string.
152 * @param scanner the string scanner to parse from (modified to point to
153 * the next character after the parsed type)
154 * @return the type encoded by scanner
155 */
156 //@ requires scanner != null;
157 //@ ensures \result != null;
158 //@ ensures \result.syntax;
159 private static Type parseType(StringScanner scanner)
160 throws ClassFormatError
161 {
162 // parse the type according to the leading character
163 // it would be more efficient to share primitive types ???
164
165 String s = scanner.s;
166 int index = scanner.index;
167
168 if (index>=s.length())
169 throw new ClassFormatError("empty type descriptor");
170
171 switch (s.charAt(index))
172 {
173 case 'B':
174 scanner.index++;
175 return JavafePrimitiveType.make(TagConstants.BYTETYPE, classLocation);
176
177 case 'C':
178 scanner.index++;
179 return JavafePrimitiveType.make(TagConstants.CHARTYPE, classLocation);
180
181 case 'D':
182 scanner.index++;
183 return JavafePrimitiveType.make(TagConstants.DOUBLETYPE, classLocation);
184
185 case 'F':
186 scanner.index++;
187 return JavafePrimitiveType.make(TagConstants.FLOATTYPE, classLocation);
188
189 case 'I':
190 scanner.index++;
191 return JavafePrimitiveType.make(TagConstants.INTTYPE, classLocation);
192
193 case 'J':
194 scanner.index++;
195 return JavafePrimitiveType.make(TagConstants.LONGTYPE, classLocation);
196
197 case 'L':
198 {
199 // extract the class name and parse it
200
201 int start = index+1, stop = s.indexOf(';', start);
202
203 if (stop<0)
204 throw new ClassFormatError("unterminated type name");
205
206 scanner.index = stop+1;
207
208 return parseClass(s.substring(start, stop));
209 }
210
211 case 'S':
212 scanner.index++;
213 return JavafePrimitiveType.make(TagConstants.SHORTTYPE, classLocation);
214
215 case 'Z':
216 scanner.index++;
217 return JavafePrimitiveType.make(TagConstants.BOOLEANTYPE, classLocation);
218
219 case '[':
220 // parse the element type and construct an array type from it
221
222 scanner.index++;
223
224 return ArrayType.make(parseType(scanner), classLocation);
225
226 default:
227 throw new ClassFormatError("unknown type character");
228 }
229 }
230
231 /**
232 * Parse a type from a given class-file return descriptor string.
233 * @param scanner the string scanner to parse from (modified to point to
234 * the next character after the parsed type)
235 * @return the type encoded by scanner
236 */
237 //@ requires scanner != null;
238 //@ ensures \result != null;
239 //@ ensures \result.syntax;
240 private static Type parseReturn(StringScanner scanner)
241 throws ClassFormatError
242 {
243 // look for the void return descriptor
244
245 String s = scanner.s;
246 int index = scanner.index;
247
248 if (index>=s.length())
249 throw new ClassFormatError("empty return descriptor");
250
251 if (s.charAt(index)=='V')
252 {
253 scanner.index++;
254 return JavafePrimitiveType.make(TagConstants.VOIDTYPE, classLocation);
255 }
256
257 return parseType(scanner);
258 }
259
260 }
261
262 /* -------------------------------------------------------------------------
263 * StringScanner
264 * ------------------------------------------------------------------------- */
265
266 /**
267 * A string and the index of the next character to scan from it.
268 */
269
270 class StringScanner {
271
272 /* -- package instance methods ------------------------------------------- */
273
274 /**
275 * Construct a new string scanner from a given string.
276 * @param s the string
277 */
278 //@ requires s != null;
279 StringScanner(String s)
280 {
281 this.s = s;
282 }
283
284 /* -- package instance variables ----------------------------------------- */
285
286 /**
287 * The string to be scanned.
288 * Initialized by constructor.
289 */
290 //@ invariant s != null;
291 String s;
292
293 /**
294 * The index of the next character to scan.
295 */
296 int index = 0;
297
298 }
299