    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