1 /*
   2  * Copyright (c) 1996, 2021, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 package org.openjdk.asmtools.jasm;
  24 
  25 import org.openjdk.asmtools.common.Module;
  26 
  27 import java.io.IOException;
  28 import java.util.ArrayList;
  29 import java.util.HashSet;
  30 import java.util.Set;
  31 import java.util.function.BiConsumer;
  32 import java.util.function.Consumer;
  33 
  34 import static org.openjdk.asmtools.jasm.ConstantPool.*;
  35 import static org.openjdk.asmtools.jasm.JasmTokens.Token;
  36 import static org.openjdk.asmtools.jasm.JasmTokens.Token.*;
  37 import static org.openjdk.asmtools.jasm.RuntimeConstants.*;
  38 import static org.openjdk.asmtools.jasm.Tables.*;
  39 
  40 /**
  41  * This class is used to parse Jasm statements and expressions.
  42  * The result is a parse tree.<p>
  43  * <p>
  44  * This class implements an operator precedence parser. Errors are
  45  * reported to the Environment object, if the error can't be
  46  * resolved immediately, a SyntaxError exception is thrown.<p>
  47  * <p>
  48  * Error recovery is implemented by catching Scanner.SyntaxError exceptions
  49  * and discarding input scanner.tokens until an input token is reached that
  50  * is possibly a legal continuation.<p>
  51  * <p>
  52  * The parse tree that is constructed represents the input
  53  * exactly (no rewrites to simpler forms). This is important
  54  * if the resulting tree is to be used for code formatting in
  55  * a programming environment. Currently only documentation comments
  56  * are retained.<p>
  57  * <p>
  58  * A parser owns several components (scanner, constant-parser,
  59  * instruction-parser, annotations-parser) to which it delegates certain
  60  * parsing responsibilities.  This parser contains functions to parse the
  61  * overall form of a class, and any members (fields, methods, inner-classes).
  62  * <p>
  63  * <p>
  64  * Syntax errors, should always be caught inside the
  65  * parser for error recovery.
  66  */
  67 class Parser extends ParseBase {
  68 
  69     /* Parser Fields */
  70     protected ConstantPool pool = null;
  71 
  72     ClassData cd = null;
  73 
  74     CodeAttr curCode;
  75 
  76     private ArrayList<ClassData> clsDataList = new ArrayList<>();
  77     private String pkg = null;
  78     private String pkgPrefix = "";
  79     private ArrayList<AnnotationData> pkgAnnttns = null;
  80     private ArrayList<AnnotationData> clsAnnttns = null;
  81     private ArrayList<AnnotationData> memberAnnttns = null;
  82     private boolean explicitcp = false;
  83     private ModuleAttr moduleAttribute;
  84     private CFVersion currentCFV;
  85     /**
  86      * other parser components
  87      */
  88     private ParserAnnotation annotParser;       // For parsing Annotations
  89     private ParserCP cpParser;                  // for parsing Constants
  90     private ParserInstr instrParser;            // for parsing Instructions
  91 
  92 
  93     /**
  94      * Create a parser
  95      */
  96     protected Parser(Environment sf, CFVersion cfVersion) throws IOException {
  97         super.init(new Scanner(sf), this, sf);
  98         this.currentCFV = cfVersion;
  99         this.annotParser = new ParserAnnotation(scanner, this, env);
 100         this.cpParser = new ParserCP(scanner, this, env);
 101         this.instrParser = new ParserInstr(scanner, this, cpParser, env);
 102     }
 103 
 104     void setDebugFlags(boolean debugScanner, boolean debugMembers,
 105                        boolean debugCP, boolean debugAnnot, boolean debugInstr) {
 106 
 107         enableDebug(debugMembers);
 108         scanner.enableDebug(debugScanner);
 109         cpParser.enableDebug(debugCP);
 110         annotParser.enableDebug(debugAnnot);
 111         instrParser.enableDebug(debugInstr);
 112     }
 113 
 114     String encodeClassString(String classname) {
 115         return "L" + classname + ";";
 116     }
 117 
 118 
 119     /*-------------------------------------------------------- */
 120 
 121     /**
 122      * Parses version in package statements
 123      */
 124 
 125     private void parseVersionPkg() throws IOException {
 126         if (scanner.token == SEMICOLON) {
 127             return;
 128         }
 129         parse_ver:
 130         {
 131             if (scanner.token != Token.VERSION) {
 132                 break parse_ver;
 133             }
 134             scanner.scan();
 135             if (scanner.token != Token.INTVAL) {
 136                 break parse_ver;
 137             }
 138             currentCFV.setMajorVersion((short) scanner.intValue);
 139             scanner.scan();
 140             if (scanner.token != Token.COLON) {
 141                 break parse_ver;
 142             }
 143             scanner.scan();
 144             if (scanner.token != Token.INTVAL) {
 145                 break parse_ver;
 146             }
 147             currentCFV.setMinorVersion((short) scanner.intValue);
 148             scanner.scan();
 149             debugScan("     [Parser.parseVersionPkg]: " + currentCFV.asString());
 150             return;
 151         }
 152         env.error(scanner.pos, "version.expected");
 153         throw new Scanner.SyntaxError();
 154     }
 155 
 156     private void parseVersion() throws IOException {
 157         if (scanner.token == Token.LBRACE) {
 158             return;
 159         }
 160         parse_ver:
 161         {
 162             if (scanner.token != Token.VERSION) {
 163                 break parse_ver;
 164             }
 165             scanner.scan();
 166             if (scanner.token != Token.INTVAL) {
 167                 break parse_ver;
 168             }
 169             cd.cfv.setMajorVersion((short) scanner.intValue);
 170             scanner.scan();
 171             if (scanner.token != Token.COLON) {
 172                 break parse_ver;
 173             }
 174             scanner.scan();
 175             if (scanner.token != Token.INTVAL) {
 176                 break parse_ver;
 177             }
 178             cd.cfv.setMinorVersion((short) scanner.intValue);
 179             scanner.scan();
 180             debugStr("parseVersion: " + cd.cfv.asString());
 181             return;
 182         }
 183         env.error(scanner.pos, "version.expected");
 184         throw new Scanner.SyntaxError();
 185     }
 186 
 187 
 188     /*---------------------------------------------*/
 189 
 190     /**
 191      * Parse an internal name: identifier.
 192      */
 193     String parseIdent() throws Scanner.SyntaxError, IOException {
 194         String v = scanner.idValue;
 195         scanner.expect(Token.IDENT);
 196         return v;
 197     }
 198 
 199     /**
 200      * Parse a local variable
 201      */
 202     void parseLocVarDef() throws Scanner.SyntaxError, IOException {
 203         if (scanner.token == Token.INTVAL) {
 204             int v = scanner.intValue;
 205             scanner.scan();
 206             curCode.LocVarDataDef(v);
 207         } else {
 208             String name = scanner.stringValue, type;
 209             scanner.expect(Token.IDENT);
 210             if (scanner.token == Token.COLON) {
 211                 scanner.scan();
 212                 type = parseIdent();
 213             } else {
 214                 type = "I";                  // TBD
 215             }
 216             curCode.LocVarDataDef(name, pool.FindCellAsciz(type));
 217         }
 218     }
 219 
 220     Argument parseLocVarRef() throws Scanner.SyntaxError, IOException {
 221         if (scanner.token == Token.INTVAL) {
 222             int v = scanner.intValue;
 223             scanner.scan();
 224             return new Argument(v);
 225         } else {
 226             String name = scanner.stringValue;
 227             scanner.expect(Token.IDENT);
 228             return curCode.LocVarDataRef(name);
 229         }
 230     }
 231 
 232     void parseLocVarEnd() throws Scanner.SyntaxError, IOException {
 233         if (scanner.token == Token.INTVAL) {
 234             int v = scanner.intValue;
 235             scanner.scan();
 236             curCode.LocVarDataEnd(v);
 237         } else {
 238             String name = scanner.stringValue;
 239             scanner.expect(Token.IDENT);
 240             curCode.LocVarDataEnd(name);
 241         }
 242     }
 243 
 244     void parseMapItem(DataVector map) throws Scanner.SyntaxError, IOException {
 245         StackMapType itemType = stackMapType(scanner.intValue, null);
 246         ConstType tag = null;
 247         Argument arg = null;
 248         Token ptoken = scanner.token;
 249         int iValue = scanner.intValue;
 250         String sValue = scanner.stringValue;
 251         scanner.scan();
 252         resolve:
 253         {
 254             switch (ptoken) {
 255                 case INTVAL:
 256                     break resolve;
 257                 case CLASS:
 258                     itemType = StackMapType.ITEM_Object;
 259                     tag = ConstType.CONSTANT_CLASS;
 260                     break resolve;
 261                 case CPINDEX:
 262                     itemType = StackMapType.ITEM_Object;
 263                     arg = pool.getCell(iValue);
 264                     break resolve;
 265                 case IDENT:
 266                     itemType = stackMapType(sValue);
 267                     tag = Tables.tag(sValue);
 268                     if (itemType != null) { // itemType OK
 269                         if ((tag != null) // ambiguity: "int," or "int 77,"?
 270                                 && (scanner.token != SEMICOLON)
 271                                 && (scanner.token != COMMA)) {
 272                             itemType = StackMapType.ITEM_Object;
 273                         }
 274                         break resolve;
 275                     } else if (tag != null) { // tag OK
 276                         itemType = StackMapType.ITEM_Object;
 277                         break resolve;
 278                     }
 279             }
 280             // resolution failed:
 281             itemType = StackMapType.ITEM_Bogus;
 282             env.error("itemtype.expected", "<" + ptoken.printValue() + ">");
 283         }
 284         switch (itemType) {
 285             case ITEM_Object:  // followed by CP index
 286                 if (arg == null) {
 287                     arg = pool.FindCell(cpParser.parseConstValue(tag));
 288                 }
 289                 map.addElement(new StackMapData.StackMapItem2(itemType, arg));
 290                 break;
 291             case ITEM_NewObject:  // followed by label
 292                 arg = instrParser.parseLabelRef();
 293                 map.addElement(new StackMapData.StackMapItem2(itemType, arg));
 294                 break;
 295             default:
 296                 map.addElement(new StackMapData.StackMapItem1(itemType));
 297         }
 298     }
 299 
 300     /**
 301      * Parse an external name: CPINDEX, string, or identifier.
 302      */
 303     ConstCell parseName() throws Scanner.SyntaxError, IOException {
 304         debugScan("------- [Parser.parseName]: ");
 305         String v;
 306         switch (scanner.token) {
 307             case CPINDEX: {
 308                 int cpx = scanner.intValue;
 309                 scanner.scan();
 310                 return pool.getCell(cpx);
 311             }
 312             case STRINGVAL:
 313                 v = scanner.stringValue;
 314                 scanner.scan();
 315                 return pool.FindCellAsciz(v);
 316 
 317             // In many cases, Identifiers can correctly have the same
 318             // names as keywords.  We need to allow these.
 319             case OPEN:
 320             case MODULE:
 321             case VARARGS:
 322             case REQUIRES:
 323             case EXPORTS:
 324             case TO:
 325             case USES:
 326             case PROVIDES:
 327             case WITH:
 328             case OPENS:
 329 
 330             case ARRAY_TYPEPATH:
 331             case INNER_TYPE_TYPEPATH:
 332             case WILDCARD_TYPEPATH:
 333             case TYPE_ARGUMENT_TYPEPATH:
 334             case PERMITTEDSUBCLASSES:
 335             case INF:
 336             case NAN:
 337             case COMPONENT:
 338 
 339             case SYNTHETIC:
 340             case DEPRECATED:
 341             case VERSION:
 342             case BITS:
 343             case STACK:
 344             case LOCAL:
 345             case OF:
 346             case INNERCLASS:
 347             case STRICT:
 348             case FIELDREF:
 349             case METHODREF:
 350             case IDENT:
 351             case BRIDGE:
 352             case VALUE:
 353             case PRIMITIVE:
 354                 v = scanner.idValue;
 355                 scanner.scan();
 356                 return pool.FindCellAsciz(v);
 357             default:
 358                 env.error(scanner.pos, "name.expected", scanner.token);
 359                 throw new Scanner.SyntaxError();
 360         }
 361     }
 362 
 363     /**
 364      * Parses a field or method reference for method handle.
 365      */
 366     ConstCell parseMethodHandle(SubTag subtag) throws Scanner.SyntaxError, IOException {
 367         ConstCell refCell;
 368         final int pos = parser.env.pos;
 369         switch (subtag) {
 370             // If the value of the reference_kind item is
 371             // 1 (REF_getField), 2 (REF_getStatic), 3 (REF_putField)  or 4 (REF_putStatic),
 372             // then the constant_pool entry at that index must be a CONSTANT_Fieldref_info structure (4.4.2)
 373             // representing a field for which a method handle is to be created. jvms-4.4.8-200-C-A
 374             case REF_GETFIELD:
 375             case REF_GETSTATIC:
 376             case REF_PUTFIELD:
 377             case REF_PUTSTATIC:
 378                 refCell = pool.FindCell(cpParser.parseConstValue(ConstType.CONSTANT_FIELD));
 379                 break;
 380             //  If the value of the reference_kind item is
 381             //  5 (REF_invokeVirtual) or 8 (REF_newInvokeSpecial),
 382             //  then the constant_pool entry at that index must be a CONSTANT_Methodref_info structure (4.4.2)
 383             //  representing a class's method or constructor (2.9.1) for which a method handle is to be created.
 384             //  jvms-4.4.8-200-C-B
 385             case REF_INVOKEVIRTUAL:
 386             case REF_NEWINVOKESPECIAL:
 387                 cpParser.setExitImmediately(true);
 388                 refCell = cpParser.parseConstRef(ConstType.CONSTANT_METHOD, ConstType.CONSTANT_INTERFACEMETHOD);
 389                 cpParser.setExitImmediately(false);
 390                 checkReferenceIndex(pos, ConstType.CONSTANT_METHOD, null);
 391                 break;
 392             case REF_INVOKESTATIC:
 393             case REF_INVOKESPECIAL:
 394                 // CODETOOLS-7902333
 395                 // 4.4.8. The CONSTANT_MethodHandle_info Structure
 396                 // reference_index
 397                 // The value of the reference_index item must be a valid index into the constant_pool table.
 398                 // The constant_pool entry at that index must be as follows:
 399                 // If the value of the reference_kind item is 6 (REF_invokeStatic) or 7 (REF_invokeSpecial),
 400                 // then if the class file version number is less than 52.0, the constant_pool entry at that index must be
 401                 // a CONSTANT_Methodref_info structure representing a class's method for which a method handle is to be created;
 402                 // if the class file version number is 52.0 or above, the constant_pool entry at that index must be
 403                 // either a CONSTANT_Methodref_info structure or a CONSTANT_InterfaceMethodref_info structure (4.4.2)
 404                 // representing a class's or interface's method for which a method handle is to be created.
 405                 ConstType ctype01 = ConstType.CONSTANT_METHOD;
 406                 ConstType ctype02 = ConstType.CONSTANT_INTERFACEMETHOD;
 407                 if (this.cd.cfv.major_version() >= 52 && Modifiers.isInterface(this.cd.access)) {
 408                     ctype01 = ConstType.CONSTANT_INTERFACEMETHOD;
 409                     ctype02 = ConstType.CONSTANT_METHOD;
 410                 }
 411                 cpParser.setExitImmediately(true);
 412                 refCell = cpParser.parseConstRef(ctype01, ctype02);
 413                 cpParser.setExitImmediately(false);
 414                 checkReferenceIndex(pos, ctype01, ctype02);
 415                 break;
 416 
 417             case REF_INVOKEINTERFACE:
 418                 cpParser.setExitImmediately(true);
 419                 refCell = cpParser.parseConstRef(ConstType.CONSTANT_INTERFACEMETHOD, ConstType.CONSTANT_METHOD);
 420                 cpParser.setExitImmediately(false);
 421                 checkReferenceIndex(pos, ConstType.CONSTANT_INTERFACEMETHOD, null);
 422                 break;
 423             default:
 424                 // should not reach
 425                 throw new Scanner.SyntaxError();
 426         }
 427         return refCell;
 428     }
 429 
 430     /**
 431      * Check the pair reference_kind:reference_index where reference_kind is any from:
 432      * REF_invokeVirtual, REF_newInvokeSpecial, REF_invokeStatic, REF_invokeSpecial, REF_invokeInterface
 433      * and reference_index is one of [Empty], Method or InterfaceMethod
 434      * There are possible entries:
 435      * ldc Dynamic REF_newInvokeSpecial:InterfaceMethod  LdcConDyTwice."<init>":
 436      * ldc Dynamic REF_invokeInterface:LdcConDyTwice."<init>":
 437      * ldc Dynamic REF_newInvokeSpecial:Method LdcConDyTwice."<init>":
 438      * ldc MethodHandle REF_newInvokeSpecial:InterfaceMethod  LdcConDyTwice."<init>":
 439      * ldc MethodHandle REF_invokeInterface:LdcConDyTwice."<init>":
 440      * ldc MethodHandle REF_newInvokeSpecial:Method LdcConDyTwice."<init>":
 441      * invokedynamic MethodHandle REF_invokeStatic:Method java/lang/invoke/StringConcatFactory.makeConcatWithConstants:
 442      * invokedynamic MethodHandle REF_invokeStatic:java/lang/invoke/StringConcatFactory.makeConcatWithConstants
 443      * ....
 444      * @param position   the position in a source file
 445      * @param defaultTag expected reference_index tag (Method or InterfaceMethod)
 446      * @param defaultTag 2nd expected reference_index tag (Method or InterfaceMethod)
 447      */
 448     private void checkReferenceIndex(int position, ConstType defaultTag, ConstType default2Tag) {
 449         if ( ! scanner.token.in(COLON, SEMICOLON) ) {
 450             if (default2Tag != null) {
 451                 env.error(position, "wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
 452             } else {
 453                 env.error(position, "wrong.tag", defaultTag.parseKey());
 454             }
 455             throw new Scanner.SyntaxError().Fatal();
 456         }
 457     }
 458 
 459     /**
 460      * Parses a sub-tag value in method handle.
 461      */
 462     SubTag parseSubtag() throws Scanner.SyntaxError, IOException {
 463         SubTag subtag = null;
 464         switch (scanner.token) {
 465             case IDENT:
 466                 subtag = subtag(scanner.stringValue);
 467                 break;
 468             case INTVAL:
 469                 subtag = subtag(scanner.intValue);
 470                 break;
 471         }
 472         if (subtag == null) {
 473             env.error("subtag.expected");
 474             throw new Scanner.SyntaxError();
 475         }
 476         scanner.scan();
 477         return subtag;
 478     }
 479 
 480     ConstCell parseClassName(boolean uncond) throws Scanner.SyntaxError, IOException {
 481         String v;
 482         switch (scanner.token) {
 483             case CPINDEX: {
 484                 int cpx = scanner.intValue;
 485                 scanner.scan();
 486                 return pool.getCell(cpx);
 487             }
 488             case STRINGVAL:
 489                 v = scanner.stringValue;
 490                 scanner.scan();
 491                 v = prependPackage(v, uncond);
 492                 return pool.FindCellAsciz(v);
 493             // Some identifiers might coincide with token names.
 494             // these should be OK to use as identifier names.
 495             case OPEN:
 496             case MODULE:
 497             case VARARGS:
 498             case REQUIRES:
 499             case EXPORTS:
 500             case TO:
 501             case USES:
 502             case PROVIDES:
 503             case WITH:
 504             case OPENS:
 505 
 506             case ARRAY_TYPEPATH:
 507             case INNER_TYPE_TYPEPATH:
 508             case WILDCARD_TYPEPATH:
 509             case TYPE_ARGUMENT_TYPEPATH:
 510             case PERMITTEDSUBCLASSES:
 511             case INF:
 512             case NAN:
 513             case COMPONENT:
 514 
 515             case SYNTHETIC:
 516             case DEPRECATED:
 517             case VERSION:
 518             case BITS:
 519             case STACK:
 520             case LOCAL:
 521             case OF:
 522             case INNERCLASS:
 523             case STRICT:
 524             case FIELDREF:
 525             case METHODREF:
 526             case BRIDGE:
 527             case IDENT:
 528             case VALUE:
 529             case PRIMITIVE:
 530                 v = scanner.idValue;
 531                 scanner.scan();
 532                 v = prependPackage(v, uncond);
 533                 return pool.FindCellAsciz(v);
 534             default:
 535                 ConstType key = Tables.tag(scanner.token.value());
 536                 env.traceln("%%%%% Unrecognized token [" + scanner.token + "]: '" + (key == null ? "null" : key.parseKey()) + "'.");
 537                 env.error(scanner.prevPos, "name.expected", "\"" + scanner.token.parseKey() + "\"");
 538                 throw new Scanner.SyntaxError();
 539         }
 540     }
 541 
 542     private String prependPackage(String className, boolean uncond) {
 543         if (uncond || (scanner.token == Token.FIELD)) {
 544             if ((!className.contains("/"))             // class identifier doesn't contain "/"
 545                     && (!className.contains("["))) {    // class identifier doesn't contain "["
 546                 className = pkgPrefix + className; // add package
 547             }
 548         }
 549         return className;
 550     }
 551 
 552     /**
 553      * Parse a signed integer of size bytes long.
 554      * size = 1 or 2
 555      */
 556     Argument parseInt(int size) throws Scanner.SyntaxError, IOException {
 557         if (scanner.token == Token.BITS) {
 558             scanner.scan();
 559         }
 560         if (scanner.token != Token.INTVAL) {
 561             env.error(scanner.pos, "int.expected");
 562             throw new Scanner.SyntaxError();
 563         }
 564         int arg = scanner.intValue * scanner.sign;
 565         switch (size) {
 566             case 1:
 567 //                if ((arg>127)||(arg<-128)) { // 0xFF not allowed
 568                 if ((arg > 255) || (arg < -128)) { // to allow 0xFF
 569                     env.error(scanner.pos, "value.large", "1 byte");
 570                     throw new Scanner.SyntaxError();
 571                 }
 572                 break;
 573             case 2:
 574 //                if ((arg > 32767) || (arg < -32768)) { //this seems
 575 // natural but is not backward compatible. Some tests contain
 576 // expressions like:
 577 //                sipush    0x8765;
 578 
 579                 if ((arg > 65535) || (arg < -32768)) {
 580                     env.error(scanner.pos, "value.large", "2 bytes");
 581                     throw new Scanner.SyntaxError();
 582                 }
 583                 break;
 584             default:
 585                 throw new InternalError("parseInt(" + size + ")");
 586         }
 587         scanner.scan();
 588         return new Argument(arg);
 589     }
 590 
 591     /**
 592      * Parse an unsigned integer of size bytes long.
 593      * size = 1 or 2
 594      */
 595     Argument parseUInt(int size) throws Scanner.SyntaxError, IOException {
 596         if (scanner.token != Token.INTVAL) {
 597             env.error(scanner.pos, "int.expected");
 598             throw new Scanner.SyntaxError();
 599         }
 600         if (scanner.sign == -1) {
 601             env.error(scanner.pos, "neg.forbidden");
 602             throw new Scanner.SyntaxError();
 603         }
 604         int arg = scanner.intValue;
 605         switch (size) {
 606             case 1:
 607                 if (arg > 255) {
 608                     env.error(scanner.pos, "value.large", "1 byte");
 609                     throw new Scanner.SyntaxError();
 610                 }
 611                 break;
 612             case 2:
 613                 if (arg > 65535) {
 614                     env.error(scanner.pos, "value.large", "2 bytes");
 615                     throw new Scanner.SyntaxError();
 616                 }
 617                 break;
 618             default:
 619                 throw new InternalError("parseUInt(" + size + ")");
 620         }
 621         scanner.scan();
 622         return new Argument(arg);
 623     }
 624 
 625     /**
 626      * Parse constant declaration
 627      */
 628     private void parseConstDef() throws IOException {
 629         for (; ; ) {
 630             if (scanner.token == Token.CPINDEX) {
 631                 int cpx = scanner.intValue;
 632                 scanner.scan();
 633                 scanner.expect(Token.ASSIGN);
 634                 env.traceln("parseConstDef:" + cpx);
 635                 pool.setCell(cpx, cpParser.parseConstRef(null));
 636             } else {
 637                 env.error("const.def.expected");
 638                 throw new Scanner.SyntaxError();
 639             }
 640             if (scanner.token != COMMA) {
 641                 scanner.expect(SEMICOLON);
 642                 return;
 643             }
 644             scanner.scan(); // COMMA
 645         }
 646     }
 647 
 648     /**
 649      * Parse the modifiers
 650      */
 651     private int scanModifier(int mod) throws IOException {
 652         int nextmod, prevpos;
 653 
 654         while (true) {
 655             nextmod = 0;
 656             switch (scanner.token) {
 657                 case PUBLIC:
 658                     nextmod = ACC_PUBLIC;
 659                     break;
 660                 case PRIVATE:
 661                     nextmod = ACC_PRIVATE;
 662                     break;
 663                 case PROTECTED:
 664                     nextmod = ACC_PROTECTED;
 665                     break;
 666                 case STATIC:
 667                     nextmod = ACC_STATIC;
 668                     break;
 669                 case FINAL:
 670                     nextmod = ACC_FINAL;
 671                     break;
 672                 case SYNCHRONIZED:
 673                     nextmod = ACC_SYNCHRONIZED;
 674                     break;
 675                 case SUPER:
 676                     nextmod = ACC_SUPER;
 677                     break;
 678                 case VOLATILE:
 679                     nextmod = ACC_VOLATILE;
 680                     break;
 681                 case BRIDGE:
 682                     nextmod = ACC_BRIDGE;
 683                     break;
 684                 case TRANSIENT:
 685                     nextmod = ACC_TRANSIENT;
 686                     break;
 687                 case VARARGS:
 688                     nextmod = ACC_VARARGS;
 689                     break;
 690                 case NATIVE:
 691                     nextmod = ACC_NATIVE;
 692                     break;
 693                 case INTERFACE:
 694                     nextmod = ACC_INTERFACE;
 695                     break;
 696                 case ABSTRACT:
 697                     nextmod = ACC_ABSTRACT;
 698                     break;
 699                 case STRICT:
 700                     nextmod = ACC_STRICT;
 701                     break;
 702                 case ENUM:
 703                     nextmod = ACC_ENUM;
 704                     break;
 705                 case SYNTHETIC:
 706                     nextmod = ACC_SYNTHETIC;
 707                     break;
 708                 case ANNOTATION_ACCESS:
 709                     nextmod = ACC_ANNOTATION;
 710                     break;
 711 
 712                 case DEPRECATED:
 713                     nextmod = DEPRECATED_ATTRIBUTE;
 714                     break;
 715                 case MANDATED:
 716                     nextmod = ACC_MANDATED;
 717                     break;
 718                 case VALUE:
 719                     nextmod = ACC_VALUE;
 720                     break;
 721                 case PRIMITIVE:
 722                     nextmod = ACC_PRIMITIVE;
 723                     break;
 724                 default:
 725                     return nextmod;
 726             }
 727             prevpos = scanner.pos;
 728             scanner.scan();
 729             if ((mod & nextmod) == 0) {
 730                 return nextmod;
 731             }
 732             env.error(prevpos, "warn.repeated.modifier");
 733         }
 734     }
 735 
 736     int scanModifiers() throws IOException {
 737         int mod = 0, nextmod;
 738 
 739         while (true) {
 740             nextmod = scanModifier(mod);
 741             if (nextmod == 0) {
 742                 return mod;
 743             }
 744             mod = mod | nextmod;
 745         }
 746     }
 747 
 748     /**
 749      * Parse a field.
 750      */
 751     private void parseField(int mod) throws Scanner.SyntaxError, IOException {
 752         debugStr("  [Parser.parseField]: <<<Begin>>>");
 753         // check access modifiers:
 754         Modifiers.checkFieldModifiers(cd, mod, scanner.pos);
 755 
 756         while (true) {
 757             ConstCell nameCell = parseName();
 758             scanner.expect(Token.COLON);
 759             ConstCell typeCell = parseName();
 760 
 761             // Define the variable
 762             FieldData fld = cd.addField(mod, nameCell, typeCell);
 763 
 764             if (memberAnnttns != null) {
 765                 fld.addAnnotations(memberAnnttns);
 766             }
 767 
 768             // Parse the optional attribute: signature
 769             if (scanner.token == Token.COLON) {
 770                 scanner.scan();
 771                 ConstCell signatureCell = parseName();
 772                 fld.setSignatureAttr(signatureCell);
 773             }
 774 
 775             // Parse the optional initializer
 776             if (scanner.token == Token.ASSIGN) {
 777                 scanner.scan();
 778                 fld.SetValue(cpParser.parseConstRef(null));
 779             }
 780 
 781             // If the next scanner.token is a comma, then there is more
 782             debugScan("  [Parser.parseField]: Field: " + fld + " ");
 783 
 784             if (scanner.token != COMMA) {
 785                 scanner.expect(SEMICOLON);
 786                 return;
 787             }
 788             scanner.scan();
 789         }  // end while
 790     }  // end parseField
 791 
 792     /**
 793      * Scan method's signature to determine size of parameters.
 794      */
 795     private int countParams(ConstCell sigCell) throws Scanner.SyntaxError {
 796         String sig;
 797         try {
 798             ConstValue_String strConst = (ConstValue_String) sigCell.ref;
 799             sig = strConst.value;
 800         } catch (NullPointerException | ClassCastException e) {
 801             return 0; // ??? TBD
 802         }
 803         int siglen = sig.length(), k = 0, loccnt = 0, errparam = 0;
 804         boolean arraytype = false;
 805         scan:
 806         {
 807             if (k >= siglen) {
 808                 break scan;
 809             }
 810             if (sig.charAt(k) != '(') {
 811                 errparam = 1;
 812                 break scan;
 813             }
 814             for (k = 1; k < siglen; k++) {
 815                 switch (sig.charAt(k)) {
 816                     case ')':
 817                         if (arraytype) {
 818                             errparam = 2;
 819                             break scan;
 820                         }
 821                         return loccnt;
 822                     case '[':
 823                         arraytype = true;
 824                         break;
 825                     case 'B':
 826                     case 'C':
 827                     case 'F':
 828                     case 'I':
 829                     case 'S':
 830                     case 'Z':
 831                         loccnt++;
 832                         arraytype = false;
 833                         break;
 834                     case 'D':
 835                     case 'J':
 836                         loccnt++;
 837                         if (arraytype) {
 838                             arraytype = false;
 839                         } else {
 840                             loccnt++;
 841                         }
 842                         break;
 843                     case 'L':
 844                     case 'Q':
 845                         for (; ; k++) {
 846                             if (k >= siglen) {
 847                                 errparam = 3;
 848                                 break scan;
 849                             }
 850                             if (sig.charAt(k) == ';') {
 851                                 break;
 852                             }
 853                         }
 854                         loccnt++;
 855                         arraytype = false;
 856                         break;
 857                     default:
 858                         errparam = 4;
 859                         break scan;
 860                 }
 861             }
 862         }
 863         env.error(scanner.pos, "msig.malformed", Integer.toString(k), Integer.toString(errparam));
 864         return loccnt;
 865     }
 866 
 867     /**
 868      * Parse a method.
 869      */
 870     private void parseMethod(int mod) throws Scanner.SyntaxError, IOException {
 871 
 872         // The start of the method
 873         int posa = scanner.pos;
 874         debugStr("  [Parser.parseMethod]: <<<Begin>>>");
 875 
 876         ConstCell nameCell = parseName();
 877         ConstValue_String strConst = (ConstValue_String) nameCell.ref;
 878         String name = strConst.value;
 879         boolean is_clinit = name.equals("<clinit>");
 880         boolean is_init = name.equals("<init>")
 881             && !Modifiers.isStatic(mod); // TODO: not a good way to detect factories...
 882         DefaultAnnotationAttr defAnnot = null;
 883 
 884         // check access modifiers:
 885         Modifiers.checkMethodModifiers(cd, mod, posa, is_init, is_clinit);
 886 
 887         scanner.expect(Token.COLON);
 888         ConstCell typeCell = parseName();
 889         int paramcnt = countParams(typeCell);
 890         if ((!Modifiers.isStatic(mod)) && !is_clinit) {
 891             paramcnt++;
 892         }
 893         if (paramcnt > 255) {
 894             env.error(scanner.pos, "warn.msig.more255", Integer.toString(paramcnt));
 895         }
 896         // Parse throws clause
 897         ArrayList<ConstCell> exc_table = null;
 898         if (scanner.token == Token.THROWS) {
 899             scanner.scan();
 900             exc_table = new ArrayList<>();
 901             for (; ; ) {
 902                 posa = scanner.pos;
 903                 ConstCell exc = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
 904                 if (exc_table.contains(exc)) {
 905                     env.error(posa, "warn.exc.repeated");
 906                 } else {
 907                     exc_table.add(exc);
 908                     env.traceln("THROWS:" + exc.arg);
 909                 }
 910                 if (scanner.token != COMMA) {
 911                     break;
 912                 }
 913                 scanner.scan();
 914             }
 915         }
 916         if (scanner.token == Token.DEFAULT) {
 917             // need to scan the annotation value
 918             defAnnot = annotParser.parseDefaultAnnotation();
 919         }
 920 
 921         MethodData curMethod = cd.StartMethod(mod, nameCell, typeCell, exc_table);
 922         Argument max_stack = null, max_locals = null;
 923 
 924         if (scanner.token == Token.STACK) {
 925             scanner.scan();
 926             max_stack = parseUInt(2);
 927         }
 928         if (scanner.token == Token.LOCAL) {
 929             scanner.scan();
 930             max_locals = parseUInt(2);
 931         }
 932         if (scanner.token == Token.INTVAL) {
 933             annotParser.parseParamAnnots(paramcnt, curMethod);
 934         }
 935 
 936         if (scanner.token == SEMICOLON) {
 937             if ((max_stack != null) || (max_locals != null)) {
 938                 env.error("token.expected", "{");
 939             }
 940             scanner.scan();
 941         } else {
 942             scanner.expect(Token.LBRACE);
 943             curCode = curMethod.startCode(posa, paramcnt, max_stack, max_locals);
 944             while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
 945                 instrParser.parseInstr();
 946                 if (scanner.token == Token.RBRACE) {
 947                     break;
 948                 }
 949                 // code's type annotation(s)
 950                 if (scanner.token == Token.ANNOTATION) {
 951                     curCode.addAnnotations(annotParser.scanAnnotations());
 952                     break;
 953                 }
 954                 scanner.expect(SEMICOLON);
 955             }
 956             curCode.endCode();
 957             scanner.expect(Token.RBRACE);
 958         }
 959 
 960         if (defAnnot != null) {
 961             curMethod.addDefaultAnnotation(defAnnot);
 962         }
 963         if (memberAnnttns != null) {
 964             curMethod.addAnnotations(memberAnnttns);
 965         }
 966         cd.EndMethod();
 967         debugStr("  [Parser.parseMethod]: Method: " + curMethod);
 968 
 969     }  // end parseMethod
 970 
 971     /**
 972      * Parse a (CPX based) BootstrapMethod entry.
 973      */
 974     private void parseCPXBootstrapMethod() throws Scanner.SyntaxError, IOException {
 975         // Parses in the form:
 976         // BOOTSTRAPMETHOD CPX_MethodHandle (CPX_Arg)* ;
 977         if (scanner.token == Token.CPINDEX) {
 978             // CPX can be a CPX to an MethodHandle constant,
 979             int cpx = scanner.intValue;
 980             ConstCell MHCell = pool.getCell(cpx);
 981             scanner.scan();
 982             ArrayList<ConstCell> bsm_args = new ArrayList<>(256);
 983 
 984             while (scanner.token != SEMICOLON) {
 985                 if (scanner.token == Token.CPINDEX) {
 986                     bsm_args.add(pool.getCell(scanner.intValue));
 987 
 988                 } else {
 989                     // throw error, bootstrap method is not recognizable
 990                     env.error(scanner.pos, "invalid.bootstrapmethod");
 991                     throw new Scanner.SyntaxError();
 992                 }
 993                 scanner.scan();
 994             }
 995             BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args);
 996             cd.addBootstrapMethod(bsmData);
 997         } else {
 998             // throw error, bootstrap method is not recognizable
 999             env.error(scanner.pos, "invalid.bootstrapmethod");
1000             throw new Scanner.SyntaxError();
1001         }
1002     }
1003 
1004     /**
1005      * Parse a NestHost entry
1006      */
1007     private void parseNestHost() throws Scanner.SyntaxError, IOException {
1008         // Parses in the form:
1009         // NESTHOST IDENT;
1010         debugStr("  [Parser.parseNestHost]: <<<Begin>>>");
1011         String className = prependPackage(parseIdent(), true);
1012         ConstCell hostClass = pool.FindCellClassByName(className);
1013         debugScan("  [Parser.parseNestHost]: NestHost: class " + className);
1014         scanner.expect(SEMICOLON);
1015         cd.addNestHost(hostClass);
1016     }
1017 
1018     /**
1019      * Parse a list of classes belonging to the
1020      * [NestMembers | PermittedSubclasses | Preload]  entry
1021      */
1022     private void parseClasses(Consumer<ArrayList<ConstCell>> classesConsumer)
1023             throws Scanner.SyntaxError, IOException {
1024         ArrayList<ConstCell> classes = new ArrayList<>();
1025         // Parses in the form:
1026         // (NESTMEMBERS|PERMITTEDSUBCLASSES)? IDENT(, IDENT)*;
1027         debugStr("  [Parser.parseClasses]: <<<Begin>>>");
1028         while (true) {
1029             String className = prependPackage(parseIdent(), true);
1030             classes.add(pool.FindCellClassByName(className));
1031             debugScan("  [Parser.parseClasses]: class " + className);
1032             if (scanner.token != COMMA) {
1033                 scanner.expect(SEMICOLON);
1034                 classesConsumer.accept(classes);
1035                 return;
1036             }
1037             scanner.scan();
1038         }
1039     }
1040 
1041     /**
1042      * Parse the Record entry
1043      */
1044     private void parseRecord() throws Scanner.SyntaxError, IOException {
1045         // Parses in the form:
1046         // RECORD { (COMPONENT)+ }
1047         // where
1048         // COMPONENT Component (ANNOTATION)* NAME:DESCRIPTOR(:SIGNATURE)? (,|;)
1049         // NAME = (CPINDEX | IDENT)
1050         // DESCRIPTOR = (CPINDEX | STRING)
1051         // SIGNATURE  = (CPINDEX | STRING)
1052         debugScan("[Parser.parseRecord]:  Begin");
1053         scanner.expect(Token.LBRACE);
1054 
1055         ArrayList<AnnotationData> componentAnntts = null;
1056         boolean grouped = false;
1057         RecordData rd = cd.setRecord(scanner.pos);
1058 
1059         while (true) {
1060             if (scanner.token == Token.RBRACE) {
1061                 if (rd.isEmpty()) {
1062                     env.error(scanner.pos, "warn.no.components.in.record.attribute");
1063                     cd.rejectRecord();
1064                 } else if (grouped) {
1065                     env.error(scanner.pos, "grouped.component.expected");
1066                 }
1067                 scanner.scan();
1068                 break;
1069             }
1070 
1071             ConstCell nameCell, descCell, signatureCell = null;
1072             if (scanner.token == Token.ANNOTATION) {
1073                 componentAnntts = annotParser.scanAnnotations();
1074             }
1075 
1076             scanner.expect(Token.COMPONENT);
1077 
1078             nameCell = parseName();
1079             scanner.expect(Token.COLON);
1080             descCell = parseName();
1081             // Parse the optional attribute: signature
1082             if (scanner.token == Token.COLON) {
1083                 scanner.scan();
1084                 signatureCell = parseName();
1085             }
1086 
1087             rd.addComponent(nameCell, descCell, signatureCell, componentAnntts);
1088 
1089             switch (scanner.token) {
1090                 case COMMA:
1091                     grouped = true;
1092                     break;
1093                 case SEMICOLON:
1094                     grouped = false;
1095                     componentAnntts = null;
1096                     break;
1097                 default:
1098                     env.error(scanner.pos, "one.of.two.token.expected",
1099                             "<" + SEMICOLON.printValue() + ">",
1100                             "<" + COMMA.printValue() + ">");
1101                     break;
1102             }
1103             // next component
1104             scanner.scan();
1105         }  // end while
1106         debugScan("[Parser.parseRecord]:  End");
1107     }
1108 
1109     /**
1110      * Parse an inner class.
1111      */
1112     private void parseInnerClass(int mod) throws Scanner.SyntaxError, IOException {
1113         // Parses in the form:
1114         // MODIFIERS (INNERCLASSNAME =)? (INNERCLASS) (OF OUTERCLASS)? ;
1115         //
1116         // where
1117         //    INNERCLASSNAME = (IDENT | CPX_IN-CL-NM)
1118         //    INNERCLASS = (CLASS IDENT | CPX_IN-CL) (S2)
1119         //    OUTERCLASS = (CLASS IDENT | CPX_OT-CL) (S3)
1120         //
1121         // Note:
1122         //    If a class reference cannot be identified using IDENT, CPX indexes must be used.
1123 
1124         // check access modifiers:
1125         debugScan("[Parser.parseInnerClass]:  Begin ");
1126         Modifiers.checkInnerClassModifiers(cd, mod, scanner.pos);
1127 
1128         ConstCell nameCell;
1129         ConstCell innerClass = null;
1130         ConstCell outerClass = null;
1131 
1132 
1133         if (scanner.token == Token.CLASS) {
1134             nameCell = pool.getCell(0);  // no NameIndex
1135             parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
1136         } else {
1137             if ((scanner.token == Token.IDENT) || scanner.checkTokenIdent()) {
1138                 // Got a Class Name
1139                 nameCell = parseName();
1140                 parseInnerClass_s1(mod, nameCell, innerClass, outerClass);
1141             } else if (scanner.token == Token.CPINDEX) {
1142                 // CPX can be either a CPX to an InnerClassName,
1143                 // or a CPX to an InnerClassInfo
1144                 int cpx = scanner.intValue;
1145                 nameCell = pool.getCell(cpx);
1146                 ConstValue nameCellValue = nameCell.ref;
1147 
1148                 if (nameCellValue instanceof ConstValue_String) {
1149                     // got a name cell
1150                     scanner.scan();
1151                     parseInnerClass_s1(mod, nameCell, innerClass, outerClass);
1152                 } else {
1153                     // got a CPRef cell
1154                     nameCell = pool.getCell(0);  // no NameIndex
1155                     parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
1156                 }
1157             } else {
1158                 pic_error();
1159             }
1160 
1161         }
1162     }
1163 
1164     private void parseInnerClass_s1(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
1165         // next scanner.token must be '='
1166         if (scanner.token == Token.ASSIGN) {
1167             scanner.scan();
1168             parseInnerClass_s2(mod, nameCell, innerClass, outerClass);
1169         } else {
1170             pic_error();
1171         }
1172 
1173     }
1174 
1175     private void parseInnerClass_s2(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
1176         // scanner.token is either "CLASS IDENT" or "CPX_Class"
1177         if ((scanner.token == Token.CPINDEX) || (scanner.token == Token.CLASS)) {
1178             if (scanner.token == Token.CPINDEX) {
1179                 innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1180             }
1181 
1182             if (scanner.token == Token.CLASS) {
1183                 // next symbol needs to be InnerClass
1184                 scanner.scan();
1185                 innerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1186             }
1187 
1188             // See if declaration is terminated
1189             if (scanner.token == SEMICOLON) {
1190                 // InnerClass is complete, no OUTERINFO;
1191                 outerClass = pool.getCell(0);
1192                 pic_tracecreate(mod, nameCell, innerClass, outerClass);
1193                 cd.addInnerClass(mod, nameCell, innerClass, outerClass);
1194             } else if (scanner.token == Token.OF) {
1195                 // got an outer class reference
1196                 parseInnerClass_s3(mod, nameCell, innerClass, outerClass);
1197             } else {
1198                 pic_error();
1199             }
1200 
1201         } else {
1202             pic_error();
1203         }
1204 
1205     }
1206 
1207     private void parseInnerClass_s3(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) throws IOException {
1208         scanner.scan();
1209         if ((scanner.token == Token.CLASS) || (scanner.token == Token.CPINDEX)) {
1210             if (scanner.token == Token.CLASS) {
1211                 // next symbol needs to be InnerClass
1212                 scanner.scan();
1213                 outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1214             }
1215             if (scanner.token == Token.CPINDEX) {
1216                 outerClass = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1217             }
1218 
1219             if (scanner.token == SEMICOLON) {
1220                 pic_tracecreate(mod, nameCell, innerClass, outerClass);
1221                 cd.addInnerClass(mod, nameCell, innerClass, outerClass);
1222             } else {
1223                 pic_error();
1224             }
1225         } else {
1226             pic_error();
1227         }
1228     }
1229 
1230     private void pic_tracecreate(int mod, ConstCell nameCell, ConstCell innerClass, ConstCell outerClass) {
1231         // throw error, IC is not recognizable
1232         env.trace(" Creating InnerClass: [" + Modifiers.toString(mod, CF_Context.CTX_INNERCLASS) + "], ");
1233 
1234         if (nameCell != pool.getCell(0)) {
1235             ConstValue value = nameCell.ref;
1236             if (value != null) {
1237                 env.trace(value.toString() + " = ");
1238             }
1239         }
1240 
1241         ConstValue_Cell ici_val = (ConstValue_Cell) innerClass.ref;
1242         ConstCell ici_ascii = ici_val.cell;
1243         // Constant pool may not be numberized yet.
1244         //
1245         // check values before dereference on a trace.
1246         if (ici_ascii.ref == null) {
1247             env.trace("<#cpx-unresolved> ");
1248         } else {
1249             ConstValue_String cval = (ConstValue_String) ici_ascii.ref;
1250             if (cval.value == null) {
1251                 env.trace("<#cpx-0> ");
1252             } else {
1253                 env.trace(cval.value + " ");
1254             }
1255         }
1256 
1257         if (outerClass != pool.getCell(0)) {
1258             if (outerClass.arg != 0) {
1259                 ConstValue_Cell oci_val = (ConstValue_Cell) outerClass.ref;
1260                 ConstCell oci_ascii = oci_val.cell;
1261                 if (oci_ascii.ref == null) {
1262                     env.trace(" of <#cpx-unresolved>  ");
1263                 } else {
1264                     ConstValue_String cval = (ConstValue_String) oci_ascii.ref;
1265                     if (cval.value == null) {
1266                         env.trace(" of <#cpx-0>  ");
1267                     } else {
1268                         env.trace(" of " + cval.value);
1269                     }
1270                 }
1271             }
1272         }
1273 
1274         env.traceln("");
1275     }
1276 
1277     private void pic_error() {
1278         // throw error, IC is not recognizable
1279         env.error(scanner.pos, "invalid.innerclass");
1280         throw new Scanner.SyntaxError();
1281     }
1282 
1283     /**
1284      * The match() method is used to quickly match opening
1285      * brackets (ie: '(', '{', or '[') with their closing
1286      * counter part. This is useful during error recovery.<p>
1287      * <p>
1288      * Scan to a matching '}', ']' or ')'. The current scanner.token must be
1289      * a '{', '[' or '(';
1290      */
1291     private void match(Token open, Token close) throws IOException {
1292         int depth = 1;
1293 
1294         while (true) {
1295             scanner.scan();
1296             if (scanner.token == open) {
1297                 depth++;
1298             } else if (scanner.token == close) {
1299                 if (--depth == 0) {
1300                     return;
1301                 }
1302             } else if (scanner.token == Token.EOF) {
1303                 env.error(scanner.pos, "unbalanced.paren");
1304                 return;
1305             }
1306         }
1307     }
1308 
1309     /**
1310      * Recover after a syntax error in a field. This involves
1311      * discarding scanner.tokens until an EOF or a possible legal
1312      * continuation is encountered.
1313      */
1314     private void recoverField() throws Scanner.SyntaxError, IOException {
1315         while (true) {
1316             switch (scanner.token) {
1317                 case EOF:
1318                 case STATIC:
1319                 case FINAL:
1320                 case PUBLIC:
1321                 case PRIVATE:
1322                 case SYNCHRONIZED:
1323                 case TRANSIENT:
1324                 case PROTECTED:
1325                 case VOLATILE:
1326                 case NATIVE:
1327 //                case INTERFACE: see below
1328                 case ABSTRACT:
1329                 case ANNOTATION_ACCESS:
1330                     // possible begin of a field, continue
1331                     return;
1332 
1333                 case LBRACE:
1334                     match(Token.LBRACE, Token.RBRACE);
1335                     scanner.scan();
1336                     break;
1337 
1338                 case LPAREN:
1339                     match(Token.LPAREN, Token.RPAREN);
1340                     scanner.scan();
1341                     break;
1342 
1343                 case LSQBRACKET:
1344                     match(Token.LSQBRACKET, Token.RSQBRACKET);
1345                     scanner.scan();
1346                     break;
1347 
1348                 case RBRACE:
1349                 case INTERFACE:
1350                 case CLASS:
1351                 case IMPORT:
1352                 case PACKAGE:
1353                     // begin of something outside a class, panic more
1354                     endClass();
1355                     scanner.debugStr("    [Parser.recoverField]: pos: [" + scanner.pos + "]: ");
1356                     throw new Scanner.SyntaxError().Fatal();
1357                 default:
1358                     // don't know what to do, skip
1359                     scanner.scan();
1360                     break;
1361             }
1362         }
1363     }
1364 
1365     /**
1366      * Parse a class or interface declaration.
1367      */
1368     private void parseClass(int mod) throws IOException {
1369         int posa = scanner.pos;
1370         debugStr("   [Parser.parseClass]:  Begin ");
1371         // check access modifiers:
1372         Modifiers.checkClassModifiers(env, mod, scanner);
1373 
1374         if (cd == null) {
1375             cd = new ClassData(env, currentCFV.clone());
1376             pool = cd.pool;
1377         }
1378 
1379         if (clsAnnttns != null) {
1380             cd.addAnnotations(clsAnnttns);
1381         }
1382 
1383         // move the tokenizer to the identifier:
1384         if (scanner.token == Token.CLASS) {
1385             scanner.scan();
1386         } else if (scanner.token == Token.ANNOTATION) {
1387             scanner.scan();
1388             if (scanner.token == Token.INTERFACE) {
1389                 mod |= ACC_ANNOTATION | ACC_INTERFACE;
1390                 scanner.scan();
1391             } else {
1392                 env.error(scanner.prevPos, "token.expected", Token.ANNOTATION.parseKey() + Token.INTERFACE.parseKey());
1393                 throw new Scanner.SyntaxError();
1394             }
1395         }
1396 
1397         // Parse the class name
1398         ConstCell nm = cpParser.parseConstRef(ConstType.CONSTANT_CLASS, null, true);
1399 
1400         if (scanner.token == Token.FIELD) { // DOT
1401             String fileExtension;
1402             scanner.scan();
1403             switch (scanner.token) {
1404                 case STRINGVAL:
1405                     fileExtension = scanner.stringValue;
1406                     break;
1407                 case IDENT:
1408                     fileExtension = scanner.idValue;
1409                     break;
1410                 default:
1411                     env.error(scanner.pos, "name.expected");
1412                     throw new Scanner.SyntaxError();
1413             }
1414             scanner.scan();
1415             cd.fileExtension = "." + fileExtension;
1416         } else if (scanner.token == Token.MODULE) {
1417             env.error(scanner.prevPos, "token.expected", Token.OPEN.parseKey());
1418             throw new Scanner.SyntaxError();
1419         } else if (scanner.token == SEMICOLON) {
1420             // drop the semi-colon following a name
1421             scanner.scan();
1422         }
1423 
1424         // Parse extends clause
1425         ConstCell sup = null;
1426         if (scanner.token == Token.EXTENDS) {
1427             scanner.scan();
1428             sup = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1429             while (scanner.token == COMMA) {
1430                 scanner.scan();
1431                 env.error(posa, "multiple.inherit");
1432                 cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1433             }
1434         }
1435 
1436         // Parse implements clause
1437         ArrayList<Argument> impl = new ArrayList<>();
1438         if (scanner.token == Token.IMPLEMENTS) {
1439             do {
1440                 scanner.scan();
1441                 Argument intf = cpParser.parseConstRef(ConstType.CONSTANT_CLASS);
1442                 if (impl.contains(intf)) {
1443                     env.error(posa, "warn.intf.repeated", intf);
1444                 } else {
1445                     impl.add(intf);
1446                 }
1447             } while (scanner.token == COMMA);
1448         }
1449         parseVersion();
1450         scanner.expect(Token.LBRACE);
1451 
1452         // Begin a new class
1453         cd.init(mod, nm, sup, impl);
1454 
1455         // Parse constant declarations
1456 
1457         // Parse class members
1458         while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
1459             switch (scanner.token) {
1460                 case SEMICOLON:
1461                     // Empty fields are allowed
1462                     scanner.scan();
1463                     break;
1464                 case CONST:
1465                     scanner.scan();
1466                     parseConstDef();
1467                     explicitcp = true;
1468                     break;
1469                 default:   // scanner.token is some member.
1470                     parseClassMembers();
1471             }  // end switch
1472         } // while
1473         scanner.expect(Token.RBRACE);
1474         // End the class
1475         endClass();
1476     } // end parseClass
1477 
1478     /**
1479      * Parses a package or type name in a module statement(s)
1480      */
1481     private String parseTypeName() throws IOException {
1482         String name = "", field = "";
1483         while (true) {
1484             if (scanner.token.possibleModuleName()) {
1485                 name = name + field + scanner.idValue;
1486                 scanner.scan();
1487             } else {
1488                 env.error(scanner.pos, "name.expected", "\"" + scanner.token.parseKey() + "\"");
1489                 throw new Scanner.SyntaxError();
1490             }
1491             if (scanner.token == Token.FIELD) {
1492                 env.error(scanner.pos, "warn.dot.will.be.converted");
1493                 field = "/";
1494                 scanner.scan();
1495             } else {
1496                 break;
1497             }
1498         }
1499         return name;
1500     }
1501 
1502     /**
1503      * Parses a module name in a module statement(s)
1504      */
1505     private String parseModuleName() throws IOException {
1506         String name = "", field = "";
1507         while (true) {
1508             if (scanner.token.possibleModuleName()) {
1509                 name = name + field + scanner.idValue;
1510                 scanner.scanModuleStatement();
1511             } else {
1512                 env.error(scanner.pos, "module.name.expected", "\"" + scanner.token.parseKey() + "\"");
1513                 throw new Scanner.SyntaxError().Fatal();
1514             }
1515             if (scanner.token == Token.FIELD) {
1516                 field = Character.toString((char) scanner.token.value());
1517                 scanner.scanModuleStatement();
1518             } else {
1519                 break;
1520             }
1521         }
1522         return name;
1523     }
1524 
1525     /**
1526      * Parse a module declaration.
1527      */
1528     private void parseModule() throws IOException {
1529         debugStr("   [Parser.parseModule]:  Begin ");
1530         if (cd == null) {
1531             cd = new ClassData(env, currentCFV.clone());
1532             pool = cd.pool;
1533         }
1534         if (clsAnnttns != null) {
1535             cd.addAnnotations(clsAnnttns);
1536         }
1537         moduleAttribute = new ModuleAttr(cd);
1538 
1539         if (scanner.token == Token.OPEN) {
1540             moduleAttribute.openModule();
1541             scanner.scan();
1542         }
1543 
1544         // move the tokenizer to the identifier:
1545         if (scanner.token == Token.MODULE) {
1546             scanner.scanModuleStatement();
1547             // scanner.scan();
1548         } else {
1549             env.error(scanner.pos, "token.expected", Token.MODULE.parseKey());
1550             throw new Scanner.SyntaxError().Fatal();
1551         }
1552         // Parse the module name
1553         String moduleName = parseModuleName();
1554         if (moduleName.isEmpty()) {
1555             env.error(scanner.pos, "name.expected");
1556             throw new Scanner.SyntaxError().Fatal();
1557         }
1558         moduleAttribute.setModuleName(moduleName);
1559 
1560         parseVersion();
1561         scanner.expect(Token.LBRACE);
1562 
1563         // Begin a new class as module
1564         cd.initAsModule();
1565 
1566         // Parse module statement(s)
1567         while ((scanner.token != Token.EOF) && (scanner.token != Token.RBRACE)) {
1568             switch (scanner.token) {
1569                 case REQUIRES:
1570                     scanRequires(moduleAttribute.requires);
1571                     break;
1572                 case EXPORTS:
1573                     scanStatement(moduleAttribute.exports,
1574                             this::parseTypeName,
1575                             this::parseModuleName,
1576                             Token.TO,
1577                             true,
1578                             "exports.expected");
1579                     break;
1580                 case OPENS:
1581                     scanStatement(moduleAttribute.opens,
1582                             this::parseTypeName,
1583                             this::parseModuleName,
1584                             Token.TO, true, "opens.expected");
1585                     break;
1586                 case USES:
1587                     scanStatement(moduleAttribute.uses, "uses.expected");
1588                     break;
1589                 case PROVIDES:
1590                     scanStatement(moduleAttribute.provides,
1591                             this::parseTypeName,
1592                             this::parseTypeName,
1593                             Token.WITH,
1594                             false,
1595                             "provides.expected");
1596                     break;
1597                 case SEMICOLON:
1598                     // Empty fields are allowed
1599                     scanner.scan();
1600                     break;
1601                 default:
1602                     env.error(scanner.pos, "module.statement.expected");
1603                     throw new Scanner.SyntaxError().Fatal();
1604             }  // end switch
1605         } // while
1606         scanner.expect(Token.RBRACE);
1607         // End the module
1608         endModule();
1609     } // end parseModule
1610 
1611     /**
1612      * Scans  ModuleStatement: requires [transitive] [static] ModuleName ;
1613      */
1614     private void scanRequires(BiConsumer<String, Integer> action) throws IOException {
1615         int flags = 0;
1616         String mn = "";
1617         scanner.scanModuleStatement();
1618         while (scanner.token != SEMICOLON) {
1619             switch (scanner.token) {
1620                 case STATIC:
1621                     if (((flags & (1 << Module.Modifier.ACC_STATIC_PHASE.asInt())) != 0) || !mn.isEmpty()) {
1622                         env.error(scanner.pos, "requires.expected");
1623                         throw new Scanner.SyntaxError().Fatal();
1624                     }
1625                     flags |= Module.Modifier.ACC_STATIC_PHASE.asInt();
1626                     break;
1627                 case TRANSITIVE:
1628                     if (((flags & (1 << Module.Modifier.ACC_TRANSITIVE.asInt())) != 0) || !mn.isEmpty()) {
1629                         env.error(scanner.pos, "requires.expected");
1630                         throw new Scanner.SyntaxError().Fatal();
1631                     }
1632                     flags |= Module.Modifier.ACC_TRANSITIVE.asInt();
1633                     break;
1634                 case IDENT:
1635                     if (!mn.isEmpty()) {
1636                         env.error(scanner.pos, "requires.expected");
1637                         throw new Scanner.SyntaxError().Fatal();
1638                     }
1639                     mn = parseModuleName();
1640                     continue;
1641                 default:
1642                     if (mn.isEmpty() && scanner.token.possibleModuleName()) {
1643                         mn = parseModuleName();
1644                         continue;
1645                     } else {
1646                         env.error(scanner.pos, "requires.expected");
1647                         throw new Scanner.SyntaxError().Fatal();
1648                     }
1649             }
1650             scanner.scanModuleStatement();
1651         }
1652         // Token.SEMICOLON
1653         if (mn.isEmpty()) {
1654             env.error(scanner.pos, "requires.expected");
1655             throw new Scanner.SyntaxError().Fatal();
1656         }
1657         action.accept(mn, flags);
1658         scanner.scanModuleStatement();
1659     }
1660 
1661     /**
1662      * Scans  ModuleStatement: uses TypeName;
1663      */
1664     private void scanStatement(Consumer<Set<String>> action, String err) throws IOException {
1665         HashSet<String> names = scanList(() -> scanner.scan(), this::parseTypeName, err, true);
1666         // Token.SEMICOLON
1667         if (names.size() != 1) {
1668             env.error(scanner.pos, err);
1669             throw new Scanner.SyntaxError().Fatal();
1670         }
1671         action.accept(names);
1672         scanner.scan();
1673     }
1674 
1675     /**
1676      * Scans  Module Statement(s):
1677      * exports  packageName [to ModuleName {, ModuleName}] ;
1678      * opens    packageName [to ModuleName {, ModuleName}] ;
1679      * provides TypeName with TypeName [,typeName] ;
1680      */
1681     private void scanStatement(BiConsumer<String, Set<String>> action,
1682                                NameSupplier source,
1683                                NameSupplier target,
1684                                Token startList,
1685                                boolean emptyListAllowed,
1686                                String err) throws IOException {
1687         String typeName = "";
1688         HashSet<String> names = new HashSet<>();
1689         scanner.scan();
1690         while (scanner.token != SEMICOLON) {
1691             if (scanner.token == Token.IDENT) {
1692                 if (typeName.isEmpty()) {
1693                     typeName = source.get();
1694                     continue;
1695                 }
1696                 env.error(scanner.pos, err);
1697                 throw new Scanner.SyntaxError().Fatal();
1698             }
1699             if (scanner.token == startList) {
1700                 if (typeName.isEmpty()) {
1701                     env.error(scanner.pos, err);
1702                     throw new Scanner.SyntaxError().Fatal();
1703                 }
1704                 names = scanList(scanner.token == Token.TO ? () -> scanner.scanModuleStatement() : () -> scanner.scan(), target, err, false);
1705                 break;
1706             } else {
1707                 env.error(scanner.pos, err);
1708                 throw new Scanner.SyntaxError().Fatal();
1709             }
1710         }
1711         // Token.SEMICOLON
1712         if (typeName.isEmpty() || (names.isEmpty() && !emptyListAllowed)) {
1713             env.error(scanner.pos, err);
1714             throw new Scanner.SyntaxError().Fatal();
1715         }
1716         action.accept(typeName, names);
1717         scanner.scan();
1718     }
1719 
1720     /**
1721      * Scans the "to" or "with" part of ModuleStatement: exports PackageName  [to  ModuleName {, ModuleName}] ;,
1722      * opens  packageName   [to  ModuleName {, ModuleName}] ;
1723      * provides TypeName with TypeName [,typeName] ;
1724      * uses TypeName;
1725      * : [ModuleName {, ModuleName}]; , [TypeName [,typeName]]; or TypeName;
1726      */
1727     private HashSet<String> scanList(Method scanMethod, NameSupplier target, String err, boolean onlyOneElement) throws IOException {
1728         HashSet<String> names = new HashSet<>();
1729         boolean comma = false, first = true;
1730         scanMethod.call();
1731         while (scanner.token != SEMICOLON) {
1732             switch (scanner.token) {
1733                 case COMMA:
1734                     if (comma || first || onlyOneElement) {
1735                         env.error(scanner.pos, err);
1736                         throw new Scanner.SyntaxError().Fatal();
1737                     }
1738                     comma = true;
1739                     break;
1740                 case IDENT:
1741                     if (!first && !comma) {
1742                         env.error(scanner.pos, err);
1743                         throw new Scanner.SyntaxError().Fatal();
1744                     }
1745                     names.add(target.get());
1746                     comma = false;
1747                     first = false;
1748                     continue;
1749                 default:
1750                     env.error(scanner.pos, err);
1751                     throw new Scanner.SyntaxError().Fatal();
1752             }
1753             scanner.scan();
1754         }
1755         // Token.SEMICOLON
1756         if (names.isEmpty() || comma) {
1757             env.error(scanner.pos, err);
1758             throw new Scanner.SyntaxError().Fatal();
1759         }
1760         return names;
1761     }
1762 
1763     private void parseClassMembers() throws IOException {
1764         debugScan("[Parser.parseClassMembers]:  Begin ");
1765         // Parse annotations
1766         if (scanner.token == Token.ANNOTATION) {
1767             memberAnnttns = annotParser.scanAnnotations();
1768         }
1769         // Parse modifiers
1770         int mod = scanModifiers();
1771         try {
1772             switch (scanner.token) {
1773                 case FIELDREF:
1774                     scanner.scan();
1775                     parseField(mod);
1776                     break;
1777                 case METHODREF:
1778                     scanner.scan();
1779                     parseMethod(mod);
1780                     break;
1781                 case INNERCLASS:
1782                     scanner.scan();
1783                     parseInnerClass(mod);
1784                     break;
1785                 case BOOTSTRAPMETHOD:
1786                     scanner.scan();
1787                     parseCPXBootstrapMethod();
1788                     break;
1789                 case NESTHOST:
1790                     if (cd.nestHostAttributeExists()) {
1791                         env.error(scanner.pos, "extra.nesthost.attribute");
1792                         throw new Scanner.SyntaxError();
1793                     } else if (cd.nestMembersAttributesExist()) {
1794                         env.error(scanner.pos, "both.nesthost.nestmembers.found");
1795                         throw new Scanner.SyntaxError();
1796                     }
1797                     scanner.scan();
1798                     parseNestHost();
1799                     break;
1800                 case NESTMEMBERS:
1801                     if (cd.nestMembersAttributesExist()) {
1802                         env.error(scanner.pos, "extra.nestmembers.attribute");
1803                         throw new Scanner.SyntaxError();
1804                     } else if (cd.nestHostAttributeExists()) {
1805                         env.error(scanner.pos, "both.nesthost.nestmembers.found");
1806                         throw new Scanner.SyntaxError();
1807                     }
1808                     scanner.scan();
1809                     parseClasses(list -> cd.addNestMembers(list));
1810                     break;
1811                 case PERMITTEDSUBCLASSES:         // JEP 360
1812                     if (cd.nestMembersAttributesExist()) {
1813                         env.error(scanner.pos, "extra.permittedsubclasses.attribute");
1814                         throw new Scanner.SyntaxError();
1815                     }
1816                     scanner.scan();
1817                     parseClasses(list -> cd.addPermittedSubclasses(list));
1818                     break;
1819                 case RECORD:                    // JEP 359
1820                     if (cd.recordAttributeExists()) {
1821                         env.error(scanner.pos, "extra.record.attribute");
1822                         throw new Scanner.SyntaxError();
1823                     }
1824                     scanner.scan();
1825                     parseRecord();
1826                     break;
1827                 case PRELOAD:
1828                     if (cd.preloadAttributeExists()) {
1829                         env.error(scanner.pos, "extra.preload.attribute");
1830                         throw new Scanner.SyntaxError();
1831                     }
1832                     scanner.scan();
1833                     parseClasses(list -> cd.addPreloads(list));
1834                     break;
1835                 default:
1836                     env.error(scanner.pos, "field.expected");
1837                     throw new Scanner.SyntaxError();
1838             }  // end switch
1839         } catch (Scanner.SyntaxError e) {
1840             recoverField();
1841         }
1842         memberAnnttns = null;
1843     }
1844 
1845     /**
1846      * Recover after a syntax error in the file.
1847      * This involves discarding scanner.tokens until an EOF
1848      * or a possible legal continuation is encountered.
1849      */
1850     private void recoverFile() throws IOException {
1851         while (true) {
1852             env.traceln("recoverFile: scanner.token=" + scanner.token);
1853             switch (scanner.token) {
1854                 case CLASS:
1855                 case INTERFACE:
1856                     // Start of a new source file statement, continue
1857                     return;
1858 
1859                 case LBRACE:
1860                     match(Token.LBRACE, Token.RBRACE);
1861                     scanner.scan();
1862                     break;
1863 
1864                 case LPAREN:
1865                     match(Token.LPAREN, Token.RPAREN);
1866                     scanner.scan();
1867                     break;
1868 
1869                 case LSQBRACKET:
1870                     match(Token.LSQBRACKET, Token.RSQBRACKET);
1871                     scanner.scan();
1872                     break;
1873 
1874                 case EOF:
1875                     return;
1876 
1877                 default:
1878                     // Don't know what to do, skip
1879                     scanner.scan();
1880                     break;
1881             }
1882         }
1883     }
1884 
1885     /**
1886      * End class
1887      */
1888     private void endClass() {
1889         if (explicitcp) {
1890             // Fix references in the constant pool (for explicitly coded CPs)
1891             pool.fixRefsInPool();
1892             // Fix any bootstrap Method references too
1893             cd.relinkBootstrapMethods();
1894         }
1895         cd.endClass();
1896         clsDataList.add(cd);
1897         cd = null;
1898     }
1899 
1900     /**
1901      * End module
1902      */
1903     private void endModule() {
1904         cd.endModule(moduleAttribute);
1905         clsDataList.add(cd);
1906         cd = null;
1907     }
1908 
1909     final ClassData[] getClassesData() {
1910         return clsDataList.toArray(new ClassData[0]);
1911     }
1912 
1913     /**
1914      * Determines whether the JASM file is for a package-info class
1915      * or for a module-info class.
1916      * <p>
1917      * creates the correct kind of ClassData accordingly.
1918      *
1919      * @throws IOException
1920      */
1921     private void parseJasmPackages() throws IOException {
1922         try {
1923             // starting annotations could either be
1924             // a package annotation, or a class annotation
1925             if (scanner.token == Token.ANNOTATION) {
1926                 if (cd == null) {
1927                     cd = new ClassData(env, currentCFV.clone());
1928                     pool = cd.pool;
1929                 }
1930                 pkgAnnttns = annotParser.scanAnnotations();
1931             }
1932             if (scanner.token == Token.PACKAGE) {
1933                 // Package statement
1934                 scanner.scan();
1935                 int where = scanner.pos;
1936                 String id = parseIdent();
1937                 parseVersionPkg();
1938                 scanner.expect(SEMICOLON);
1939 
1940                 if (pkg == null) {
1941                     pkg = id;
1942                     pkgPrefix = id + "/";
1943                 } else {
1944                     env.error(where, "package.repeated");
1945                 }
1946                 debugScan("[Parser.parseJasmPackages] {PARSED} package-prefix: " + pkgPrefix + " ");
1947             }
1948         } catch (Scanner.SyntaxError e) {
1949             recoverFile();
1950         }
1951         // skip bogus semi colons
1952         while (scanner.token == SEMICOLON) {
1953             scanner.scan();
1954         }
1955 
1956         // checks that we compile module or package compilation unit
1957         if (scanner.token == Token.EOF) {
1958             env.traceln("Scanner:  EOF");
1959             String sourceName = env.getSimpleInputFileName();
1960             int mod = ACC_INTERFACE | ACC_ABSTRACT;
1961 
1962             // package-info
1963             if (sourceName.endsWith("package-info.jasm")) {
1964                 env.traceln("Creating \"package-info.jasm\": package: " + pkg + " " + currentCFV.asString());
1965 
1966                 if (cd == null) {
1967                     cd = new ClassData(env, currentCFV.clone());
1968                     pool = cd.pool;
1969                 } else {
1970                     cd.cfv = currentCFV.clone();
1971                 }
1972                 ConstCell me = pool.FindCellClassByName(pkgPrefix + "package-info");
1973 
1974                 // Interface package-info should be marked synthetic and abstract
1975                 if (currentCFV.major_version() > 49) {
1976                     mod |= SYNTHETIC_ATTRIBUTE;
1977                 }
1978                 cd.init(mod, me, new ConstCell(0), null);
1979 
1980                 if (pkgAnnttns != null) {
1981                     cd.addAnnotations(pkgAnnttns);
1982                 }
1983 
1984                 endClass();
1985             }
1986             return;
1987         }
1988 
1989         if (pkg == null && pkgAnnttns != null) { // RemoveModules
1990             clsAnnttns = pkgAnnttns;
1991             pkgAnnttns = null;
1992         }
1993     }
1994 
1995     /**
1996      * Parse an Jasm file.
1997      */
1998     void parseFile() {
1999         try {
2000             // First, parse any package identifiers (and associated package annotations)
2001             parseJasmPackages();
2002 
2003             while (scanner.token != Token.EOF) {
2004                 // Second, parse any class identifiers (and associated class annotations)
2005                 try {
2006                     // Parse annotations
2007                     if (scanner.token == Token.ANNOTATION) {
2008                         if (cd == null) {
2009                             cd = new ClassData(env, currentCFV.clone());
2010                             pool = cd.pool;
2011                         } else {
2012                             cd.cfv = currentCFV.clone();
2013                         }
2014                         clsAnnttns = annotParser.scanAnnotations();
2015                     }
2016 
2017                     // Parse class modifiers
2018                     int mod = scanModifiers();
2019                     if (mod == 0) {
2020                         switch (scanner.token) {
2021                             case OPEN:
2022                             case MODULE:
2023                             case CLASS:
2024                             case CPINDEX:
2025                             case STRINGVAL:
2026                             case IDENT:
2027                                 // this is a class declaration anyway
2028                                 break;
2029                             case SEMICOLON:
2030                                 // Bogus semi colon
2031                                 scanner.scan();
2032                                 continue;
2033                             default:
2034                                 // no class declaration found
2035                                 debugScan(" [Parser.parseFile]: ");
2036                                 env.error(scanner.pos, "toplevel.expected");
2037                                 throw new Scanner.SyntaxError();
2038                         }
2039                     } else if (Modifiers.isInterface(mod) && (scanner.token != Token.CLASS)) {
2040                         // rare syntactic sugar:
2041                         // interface <ident> == abstract interface class <ident>
2042                         mod |= ACC_ABSTRACT;
2043                     }
2044                     if (scanner.token == Token.MODULE || scanner.token == Token.OPEN)
2045                         parseModule();
2046                     else
2047                         parseClass(mod);
2048                     clsAnnttns = null;
2049 
2050                 } catch (Scanner.SyntaxError e) {
2051                     // KTL
2052                     env.traceln("^^^^^^^ Syntax Error ^^^^^^^^^^^^");
2053                     if (scanner.debugFlag)
2054                         e.printStackTrace();
2055                     if (e.isFatal()) {
2056                         break;
2057                     }
2058                     recoverFile();
2059                 }
2060             }
2061         } catch (IOException e) {
2062             env.error(scanner.pos, "io.exception", env.getSimpleInputFileName());
2063         } catch (Error er) {
2064             er.printStackTrace();
2065         }
2066     } //end parseFile
2067 
2068     @FunctionalInterface
2069     interface NameSupplier {
2070         String get() throws IOException;
2071     }
2072 
2073     @FunctionalInterface
2074     interface Method {
2075         void call() throws IOException;
2076     }
2077 
2078     /**
2079      * The main compile error for the parser
2080      */
2081     static class CompilerError extends Error {
2082 
2083         CompilerError(String message) {
2084             super(message);
2085         }
2086     }
2087 }  //end Parser