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 java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.function.BiFunction;
28
29 import static org.openjdk.asmtools.jasm.JasmTokens.Token;
30 import static org.openjdk.asmtools.jasm.Tables.*;
31
32 /**
33 * ParserCP
34 *
35 * ParseCP is a parser class owned by Parser.java. It is primarily responsible for parsing
36 * the constant pool and constant declarations.
37 */
38 public class ParserCP extends ParseBase {
39
40 /**
41 * Stop parsing a source file immediately and interpret any issue as an error
42 */
43 private boolean exitImmediately = false;
44
45 /**
46 * local handles on the scanner, main parser, and the error reporting env
47 */
48 /**
49 * Visitor object
50 */
51 private ParserCPVisitor pConstVstr;
52 /**
53 * counter of left braces
54 */
55 private int lbrace = 0;
56
57
58 /**
59 * main constructor
60 *
61 * @param scanner
62 * @param parser
63 * @param env
64 */
65 protected ParserCP(Scanner scanner, Parser parser, Environment env) {
66 super.init(scanner, parser, env);
67 pConstVstr = new ParserCPVisitor();
68 }
69
70 /**
71 * In particular cases it's necessary to interpret a warning issue as an error and
72 * stop parsing a source file immediately
73 * cpParser.setExitImmediately(true);
74 * çparseConstRef(...);
75 * cpParser.setExitImmediately(false);
76 */
77 public void setExitImmediately(boolean exitImmediately) {
78 this.exitImmediately = exitImmediately;
79 }
80
81 public boolean isExitImmediately() {
82 return exitImmediately;
83 }
84
85 /**
86 * ParserCPVisitor
87 *
88 * This inner class overrides a constant pool visitor to provide specific parsing
89 * instructions (per method) for each type of Constant.
90 *
91 * Note: since the generic visitor throws no exceptions, this derived class tunnels
92 * the exceptions, rethrown in the visitEcept method.
93 */
94 class ParserCPVisitor extends ConstantPool.CPTagVisitor<ConstantPool.ConstValue> {
95
96 private IOException IOProb;
97 private Scanner.SyntaxError SyProb;
98
99
100 public ParserCPVisitor() {
101 IOProb = null;
102 SyProb = null;
103 }
104
105 //This is the entry point for a visitor that tunnels exceptions
106 public ConstantPool.ConstValue visitExcept(ConstType tag) throws IOException, Scanner.SyntaxError {
107 IOProb = null;
108 SyProb = null;
109 debugStr("------- [ParserCPVisitor.visitExcept]: ");
110 ConstantPool.ConstValue ret = visit(tag);
111
112 if (IOProb != null) {
113 throw IOProb;
114 }
115
116 if (SyProb != null) {
117 throw SyProb;
118 }
119
120 return ret;
121 }
122
123 @Override
124 public ConstantPool.ConstValue visitUTF8(ConstType tag) {
125 debugStr("------- [ParserCPVisitor.visitUTF8]: ");
126 try {
127 scanner.expect(Token.STRINGVAL);
128 } catch (IOException e) {
129 IOProb = e;
130 }
131 ConstantPool.ConstValue_String obj
132 = new ConstantPool.ConstValue_String(scanner.stringValue);
133 return obj;
134 }
135
136 @Override
137 public ConstantPool.ConstValue visitInteger(ConstType tag) {
138 debugStr("------- [ParserCPVisitor.visitInteger]: ");
139 ConstantPool.ConstValue_Integer obj;
140 int v = 0;
141 try {
142 if (scanner.token == Token.BITS) {
143 scanner.scan();
144 scanner.inBits = true;
145 }
146 v = scanner.intValue * scanner.sign;
147 scanner.expect(Token.INTVAL);
148 } catch (IOException e) {
149 IOProb = e;
150 }
151 obj = new ConstantPool.ConstValue_Integer(tag, v);
152 return obj;
153 }
154
155 @Override
156 public ConstantPool.ConstValue visitLong(ConstType tag) {
157 debugStr("------- [ParserCPVisitor.visitLong]: ");
158 ConstantPool.ConstValue_Long obj = null;
159 try {
160 long v;
161 if (scanner.token == Token.BITS) {
162 scanner.scan();
163 scanner.inBits = true;
164 }
165 switch (scanner.token) {
166 case INTVAL:
167 v = scanner.intValue;
168 break;
169 case LONGVAL:
170 v = scanner.longValue;
171 break;
172 default:
173 env.error(scanner.prevPos, "token.expected", "Integer");
174 throw new Scanner.SyntaxError();
175 }
176 obj = new ConstantPool.ConstValue_Long(tag, v * scanner.sign);
177 scanner.scan();
178 } catch (IOException e) {
179 IOProb = e;
180 } catch (Scanner.SyntaxError e) {
181 SyProb = e;
182 }
183 return obj;
184 }
185
186 @Override
187 public ConstantPool.ConstValue visitFloat(ConstType tag) {
188 debugStr("------- [ParserCPVisitor.visitFloat]: ");
189 ConstantPool.ConstValue_Integer obj = null;
190 try {
191 int v;
192 float f;
193 scanner.inBits = false; // this needs to be initialized for each float!
194 if (scanner.token == Token.BITS) {
195 scanner.scan();
196 scanner.inBits = true;
197 }
198 i2f: {
199 switch (scanner.token) {
200 case INTVAL:
201 if (scanner.inBits) {
202 v = scanner.intValue;
203 break i2f;
204 } else {
205 f = (float) scanner.intValue;
206 break;
207 }
208 case FLOATVAL:
209 f = scanner.floatValue;
210 break;
211 case DOUBLEVAL:
212 f = (float) scanner.doubleValue; // to be excluded?
213 break;
214 case INF:
215 f = Float.POSITIVE_INFINITY;
216 break;
217 case NAN:
218 f = Float.NaN;
219 break;
220 default:
221 env.traceln("token=" + scanner.token);
222 env.error(scanner.pos, "token.expected", "<Float>");
223 throw new Scanner.SyntaxError();
224 }
225 v = Float.floatToIntBits(f);
226 }
227 if (scanner.sign == -1) {
228 v = v ^ 0x80000000;
229 }
230 obj = new ConstantPool.ConstValue_Integer(tag, v);
231 scanner.scan();
232 } catch (IOException e) {
233 IOProb = e;
234 } catch (Scanner.SyntaxError e) {
235 SyProb = e;
236 }
237 return obj;
238 }
239
240 @Override
241 public ConstantPool.ConstValue visitDouble(ConstType tag) {
242 debugStr("------- [ParserCPVisitor.visitDouble]: ");
243 ConstantPool.ConstValue_Long obj = null;
244 try {
245 long v;
246 double d;
247 if (scanner.token == Token.BITS) {
248 scanner.scan();
249 scanner.inBits = true;
250 }
251 d2l: {
252 switch (scanner.token) {
253 case INTVAL:
254 if (scanner.inBits) {
255 v = scanner.intValue;
256 break d2l;
257 } else {
258 d = scanner.intValue;
259 break;
260 }
261 case LONGVAL:
262 if (scanner.inBits) {
263 v = scanner.longValue;
264 break d2l;
265 } else {
266 d = (double) scanner.longValue;
267 break;
268 }
269 case FLOATVAL:
270 d = scanner.floatValue;
271 break;
272 case DOUBLEVAL:
273 d = scanner.doubleValue;
274 break;
275 case INF:
276 d = Double.POSITIVE_INFINITY;
277 break;
278 case NAN:
279 d = Double.NaN;
280 break;
281 default:
282 env.error(scanner.pos, "token.expected", "Double");
283 throw new Scanner.SyntaxError();
284 }
285 v = Double.doubleToLongBits(d);
286 }
287 if (scanner.sign == -1) {
288 v = v ^ 0x8000000000000000L;
289 }
290 obj = new ConstantPool.ConstValue_Long(tag, v);
291 scanner.scan();
292 } catch (IOException e) {
293 IOProb = e;
294 } catch (Scanner.SyntaxError e) {
295 SyProb = e;
296 }
297 return obj;
298 }
299
300 private ConstantPool.ConstCell visitName(ConstType tag) {
301 debugStr("------- [ParserCPVisitor.visitName]: ");
302 ConstantPool.ConstCell obj = null;
303 try {
304 obj = parser.parseName();
305 } catch (IOException e) {
306 IOProb = e;
307 }
308 return obj;
309 }
310
311 @Override
312 public ConstantPool.ConstValue visitMethodtype(ConstType tag) {
313 debugStr("------- [ParserCPVisitor.visitMethodtype]: ");
314 ConstantPool.ConstValue_Cell obj = null;
315 ConstantPool.ConstCell cell = visitName(tag);
316 if (IOProb == null) {
317 obj = new ConstantPool.ConstValue_Cell(tag, cell);
318 }
319 return obj;
320 }
321
322 @Override
323 public ConstantPool.ConstValue visitString(ConstType tag) {
324 debugStr("------- [ParserCPVisitor.visitString]: ");
325 ConstantPool.ConstValue_Cell obj = null;
326 ConstantPool.ConstCell cell = visitName(tag);
327 if (IOProb == null) {
328 obj = new ConstantPool.ConstValue_Cell(tag, cell);
329 }
330 return obj;
331 }
332
333 @Override
334 public ConstantPool.ConstValue visitClass(ConstType tag) {
335 debugStr("------- [ParserCPVisitor.visitClass]: ");
336 ConstantPool.ConstValue_Cell obj = null;
337 try {
338 ConstantPool.ConstCell cell = parser.parseClassName(true);
339 obj = new ConstantPool.ConstValue_Cell(tag, cell);
340 } catch (IOException e) {
341 IOProb = e;
342 }
343 return obj;
344 }
345
346 @Override
347 public ConstantPool.ConstValue visitMethodhandle(ConstType tag) {
348 debugStr("------- [ParserCPVisitor.visitMethodHandle]: ");
349 ConstantPool.ConstValue_Pair obj = null;
350 try {
351 ConstantPool.ConstCell refCell;
352 ConstantPool.ConstCell subtagCell;
353 SubTag subtag;
354 // MethodHandle [INVOKESUBTAG|INVOKESUBTAG_INDEX] : CONSTANT_FIELD | [FIELDREF|METHODREF|INTERFACEMETHODREF]
355 if (scanner.token == Token.INTVAL) {
356 // INVOKESUBTAG_INDEX
357 // Handle explicit constant pool form
358 subtag = subtag(scanner.intValue);
359 subtagCell = new ConstantPool.ConstCell(subtag.value());
360 scanner.scan();
361 scanner.expect(Token.COLON);
362 if (scanner.token == Token.CPINDEX) {
363 // CONSTANT_FIELD
364 int cpx = scanner.intValue;
365 refCell = parser.pool.getCell(cpx);
366 scanner.scan();
367 } else {
368 // [FIELDREF|METHODREF|INTERFACEMETHODREF]
369 refCell = parser.parseMethodHandle(subtag);
370 }
371 } else {
372 // INVOKESUBTAG : REF_INVOKEINTERFACE, REF_NEWINVOKESPECIAL, ...
373 // normal JASM
374 subtag = parser.parseSubtag();
375 subtagCell = new ConstantPool.ConstCell(subtag.value());
376 scanner.expect(Token.COLON);
377 if (scanner.token == Token.CPINDEX) {
378 // CODETOOLS-7901522: Jasm doesn't allow to create REF_invoke* referring an InterfaceMethod
379 // Parsing the case when refCell is CP index (#1)
380 // const #1 = InterfaceMethod m:"()V";
381 // const #2 = MethodHandle REF_invokeSpecial:#1;
382 int cpx = scanner.intValue;
383 refCell = parser.pool.getCell(cpx);
384 scanner.scan();
385 } else {
386 refCell = parser.parseMethodHandle(subtag);
387 }
388 }
389 obj = new ConstantPool.ConstValue_Pair(tag, subtagCell, refCell);
390 } catch (IOException e) {
391 IOProb = e;
392 }
393 return obj;
394 }
395
396 private ConstantPool.ConstValue_Pair visitMember(ConstType tag) {
397 debugStr("------- [ParserCPVisitor.visitMember]: ");
398 ConstantPool.ConstValue_Pair obj = null;
399 try {
400 Token prevtoken = scanner.token;
401 ConstantPool.ConstCell firstName, ClassCell, NameCell, NapeCell;
402 firstName = parser.parseClassName(false);
403 if (scanner.token == Token.FIELD) { // DOT
404 scanner.scan();
405 if (prevtoken == Token.CPINDEX) {
406 ClassCell = firstName;
407 } else {
408 ClassCell = parser.pool.FindCell(ConstType.CONSTANT_CLASS, firstName);
409 }
410 NameCell = parser.parseName();
411 } else {
412 // no class provided - assume current class
413 ClassCell = parser.cd.me;
414 NameCell = firstName;
415 }
416 if (scanner.token == Token.COLON) {
417 // name and type separately
418 scanner.scan();
419 NapeCell = parser.pool.FindCell(ConstType.CONSTANT_NAMEANDTYPE, NameCell, parser.parseName());
420 } else {
421 // name and type as single name
422 NapeCell = NameCell;
423 }
424 obj = new ConstantPool.ConstValue_Pair(tag, ClassCell, NapeCell);
425 } catch (IOException e) {
426 IOProb = e;
427 }
428 return obj;
429 }
430
431 @Override
432 public ConstantPool.ConstValue visitField(ConstType tag) {
433 debugStr("------- [ParserCPVisitor.visitField]: ");
434 return visitMember(tag);
435 }
436
437 @Override
438 public ConstantPool.ConstValue visitMethod(ConstType tag) {
439 debugStr("------- [ParserCPVisitor.visitMethod]: ");
440 return visitMember(tag);
441 }
442
443 @Override
444 public ConstantPool.ConstValue visitInterfacemethod(ConstType tag) {
445 debugStr("------- [ParserCPVisitor.visitInterfacemethod]: ");
446 return visitMember(tag);
447 }
448
449 @Override
450 public ConstantPool.ConstValue visitNameandtype(ConstType tag) {
451 debugStr("------- [ParserCPVisitor.visitNameandtype]: ");
452 ConstantPool.ConstValue_Pair obj = null;
453 try {
454 ConstantPool.ConstCell NameCell = parser.parseName(), TypeCell;
455 scanner.expect(Token.COLON);
456 TypeCell = parser.parseName();
457 obj = new ConstantPool.ConstValue_Pair(tag, NameCell, TypeCell);
458 } catch (IOException e) {
459 IOProb = e;
460 }
461 return obj;
462 }
463
464 @Override
465 public ConstantPool.ConstValue_IndyPair visitInvokedynamic(ConstType tag) {
466 debugStr("------- [ParserCPVisitor.visitInvokeDynamic]: ");
467 final BiFunction<BootstrapMethodData, ConstantPool.ConstCell, ConstantPool.ConstValue_IndyPair> ctor =
468 (bsmData, napeCell) -> new ConstantPool.ConstValue_IndyPair(bsmData, napeCell);
469 return visitBsm(ctor);
470 }
471
472 @Override
473 public ConstantPool.ConstValue_CondyPair visitDynamic(ConstType tag) {
474 debugStr("------- [ParserCPVisitor.visitDynamic]: ");
475 final BiFunction<BootstrapMethodData, ConstantPool.ConstCell, ConstantPool.ConstValue_CondyPair> ctor =
476 (bsmData, napeCell) -> new ConstantPool.ConstValue_CondyPair(bsmData, napeCell);
477 return visitBsm(ctor);
478 }
479
480 private <E extends ConstantPool.ConstValue_IndyOrCondyPair> E visitBsm(BiFunction<BootstrapMethodData, ConstantPool.ConstCell, E> ctor) {
481 E obj = null;
482 try {
483 if (scanner.token == Token.INTVAL) {
484 // Handle explicit constant pool form
485 int bsmIndex = scanner.intValue;
486 scanner.scan();
487 scanner.expect(Token.COLON);
488 if (scanner.token != Token.CPINDEX) {
489 env.traceln("token=" + scanner.token);
490 env.error(scanner.pos, "token.expected", "<CPINDEX>");
491 throw new Scanner.SyntaxError();
492 }
493 int cpx = scanner.intValue;
494 scanner.scan();
495 // Put a placeholder in place of BSM.
496 // resolve placeholder after the attributes are scanned.
497 BootstrapMethodData bsmData = new BootstrapMethodData(bsmIndex);
498 obj = ctor.apply(bsmData, parser.pool.getCell(cpx));
499 } else {
500 // Handle full form
501 ConstantPool.ConstCell MHCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_METHODHANDLE));
502 scanner.expect(Token.COLON);
503 ConstantPool.ConstCell NapeCell = parser.pool.FindCell(parseConstValue(ConstType.CONSTANT_NAMEANDTYPE));
504 if(scanner.token == Token.LBRACE) {
505 ParserCP.this.lbrace++;
506 scanner.scan();
507 }
508 ArrayList<ConstantPool.ConstCell> bsm_args = new ArrayList<>(256);
509 while(true) {
510 if( ParserCP.this.lbrace > 0 ) {
511 if(scanner.token == Token.RBRACE ) {
512 ParserCP.this.lbrace--;
513 scanner.scan();
514 break;
515 } else if(scanner.token == Token.SEMICOLON) {
516 scanner.expect(Token.RBRACE);
517 }
518 } else if(scanner.token == Token.SEMICOLON) {
519 break;
520 }
521 if (scanner.token == Token.COMMA) {
522 scanner.scan();
523 }
524 bsm_args.add(parseConstRef(null));
525 }
526 if( ParserCP.this.lbrace == 0 ) {
527 scanner.check(Token.SEMICOLON);
528 }
529 BootstrapMethodData bsmData = new BootstrapMethodData(MHCell, bsm_args);
530 parser.cd.addBootstrapMethod(bsmData);
531 obj = ctor.apply(bsmData, NapeCell);
532 }
533 } catch (IOException e) {
534 IOProb = e;
535 }
536 return obj;
537 }
538 } // End Visitor
539
540 /**
541 * Parse CONSTVALUE
542 */
543 protected ConstantPool.ConstValue parseConstValue(ConstType tag) throws IOException, Scanner.SyntaxError {
544 return pConstVstr.visitExcept(tag);
545 }
546
547 /**
548 * Parse [TAG] CONSTVALUE
549 */
550 protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag) throws Scanner.SyntaxError, IOException {
551 return parseTagConstValue(defaultTag, null, false);
552 }
553
554 private ConstType scanConstByID(boolean ignoreKeywords) {
555 ConstType tag = null;
556 if (!ignoreKeywords) {
557 ConstType tg = Tables.tag(scanner.idValue);
558 if (tg != null) {
559 tag = tg;
560 }
561 debugStr(" *^*^*^*^ [ParserCP.scanConst]: {TAG = " + (tg == null ? "null" : tg.toString()) + " ");
562 }
563 return tag;
564 }
565
566 private ConstType scanConstPrimVal() throws Scanner.SyntaxError, IOException {
567 ConstType tag = null;
568 switch (scanner.token) {
569 case INTVAL:
570 tag = ConstType.CONSTANT_INTEGER;
571 break;
572 case LONGVAL:
573 tag = ConstType.CONSTANT_LONG;
574 break;
575 case FLOATVAL:
576 tag = ConstType.CONSTANT_FLOAT;
577 break;
578 case DOUBLEVAL:
579 tag = ConstType.CONSTANT_DOUBLE;
580 break;
581 case STRINGVAL:
582 case BITS:
583 case IDENT:
584 tag = ConstType.CONSTANT_STRING;
585 break;
586 default:
587 // problem - no constant value
588 System.err.println("NEAR: " + scanner.token.printValue());
589 env.error(scanner.pos, "value.expected");
590 throw new Scanner.SyntaxError();
591 }
592 return tag;
593 }
594
595 private void checkWrongTag(ConstType tag, ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException {
596 if (defaultTag != null) {
597 if (tag != defaultTag) {
598 if (default2Tag == null) {
599 if( exitImmediately ) {
600 env.error("wrong.tag", defaultTag.parseKey());
601 throw new Scanner.SyntaxError().Fatal();
602 }
603 env.error("warn.wrong.tag", defaultTag.parseKey());
604 } else if (tag != default2Tag) {
605 if( exitImmediately ) {
606 env.error("wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
607 throw new Scanner.SyntaxError().Fatal();
608 }
609 env.error("warn.wrong.tag2", defaultTag.parseKey(), default2Tag.parseKey());
610 }
611 }
612 }
613 }
614
615 protected ConstantPool.ConstValue parseTagConstValue(ConstType defaultTag, ConstType default2Tag, boolean ignoreKeywords) throws Scanner.SyntaxError, IOException {
616 debugScan(" *^*^*^*^ [ParserCP.parseTagConstValue]: Begin default_tag: ignoreKeywords: " + (ignoreKeywords ? "true" : "false"));
617 // Lookup the Tag from the scanner
618 ConstType tag = scanConstByID(ignoreKeywords);
619 debugStr(" *^*^*^*^ [ParserCP.parseTagConstValue]: {tag = " + tag + ", defaulttag = " + defaultTag + "} ");
620
621 // If the scanned tag is null
622 if (tag == null) {
623 // and, if the expected tag is null
624 if (defaultTag == null) {
625 // return some other type of constant as the tag
626 tag = scanConstPrimVal();
627 } else {
628 // otherwise, make the scanned-tag the same constant-type
629 // as the expected tag.
630 tag = defaultTag;
631 }
632 } else {
633 // If the scanned tag is some constant type
634 // and the scanned type does not equal the expected type
635 checkWrongTag(tag, defaultTag, default2Tag);
636 scanner.scan();
637 }
638 return parseConstValue(tag);
639 } // end parseTagConstValue
640
641 protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag) throws Scanner.SyntaxError, IOException {
642 return parseConstRef(defaultTag, null, false);
643 }
644
645 protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag, ConstType default2Tag) throws Scanner.SyntaxError, IOException {
646 return parseConstRef(defaultTag, default2Tag, false);
647 }
648
649 /**
650 * Parse an instruction argument, one of: * #NUMBER, #NAME, [TAG] CONSTVALUE
651 */
652 protected ConstantPool.ConstCell parseConstRef(ConstType defaultTag,
653 ConstType default2Tag,
654 boolean ignoreKeywords) throws Scanner.SyntaxError, IOException {
655 if (scanner.token == Token.CPINDEX) {
656 int cpx = scanner.intValue;
657 scanner.scan();
658 return parser.pool.getCell(cpx);
659 } else {
660 ConstantPool.ConstValue ref = parseTagConstValue(defaultTag, default2Tag, ignoreKeywords);
661 return parser.pool.FindCell(ref);
662 }
663 } // end parseConstRef
664
665 }