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