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 }