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 PrimitiveType.make(TagConstants.BYTETYPE, classLocation);
176    
177        case 'C':
178          scanner.index++;
179          return PrimitiveType.make(TagConstants.CHARTYPE, classLocation);
180    
181        case 'D':
182          scanner.index++;
183          return PrimitiveType.make(TagConstants.DOUBLETYPE, classLocation);
184    
185        case 'F':
186          scanner.index++;
187          return PrimitiveType.make(TagConstants.FLOATTYPE, classLocation);
188    
189        case 'I':
190          scanner.index++;
191          return PrimitiveType.make(TagConstants.INTTYPE, classLocation);
192    
193        case 'J':
194          scanner.index++;
195          return PrimitiveType.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 PrimitiveType.make(TagConstants.SHORTTYPE, classLocation);
214    
215        case 'Z':
216          scanner.index++;
217          return PrimitiveType.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 PrimitiveType.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