1 /* 2 * Copyright (c) 2009, 2020, 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.jcoder; 24 25 import java.io.*; 26 import java.util.ArrayList; 27 import java.util.Arrays; 28 import java.util.HashMap; 29 import java.util.Stack; 30 31 import static org.openjdk.asmtools.jcoder.JcodTokens.ConstType; 32 import static org.openjdk.asmtools.jcoder.JcodTokens.Token; 33 34 /** 35 * Compiles just 1 source file 36 */ 37 class Jcoder { 38 39 /*-------------------------------------------------------- */ 40 /* Jcoder Fields */ 41 private ArrayList<ByteBuffer> Classes = new ArrayList<>(); 42 private ByteBuffer buf; 43 private DataOutputStream bufstream; 44 private int depth = 0; 45 private String tabStr = ""; 46 private Context context = null; 47 protected SourceFile env; 48 protected Scanner scanner; 49 50 /*-------------------------------------------------------- */ 51 /* Jcoder inner classes */ 52 53 /*-------------------------------------------------------- */ 54 /* ContextTag (marker) - describes the type of token */ 55 /* this is rather cosmetic, no function currently. */ 56 private enum ContextTag { 57 NULL ( ""), 58 CLASS ( "Class"), 59 CONSTANTPOOL ( "Constant-Pool"), 60 INTERFACES ( "Interfaces"), 61 INTERFACE ( "Interface"), 62 METHODS ( "Methods"), 63 METHOD ( "Method"), 64 FIELDS ( "Fields"), 65 FIELD ( "Field"), 66 ATTRIBUTE ( "Attribute"); 67 68 private final String printValue; 69 70 ContextTag(String value) { 71 printValue = value; 72 } 73 74 public String printval() { 75 return printValue; 76 } 77 } 78 79 /*-------------------------------------------------------- */ 80 /* ContextVal (marker) - Specific value on a context stack */ 81 private class ContextVal { 82 83 public ContextTag tag; 84 int compCount; 85 ContextVal owner; 86 87 ContextVal(ContextTag tg) { 88 tag = tg; 89 compCount = 0; 90 owner = null; 91 } 92 93 ContextVal(ContextTag tg, ContextVal ownr) { 94 tag = tg; 95 compCount = 0; 96 owner = ownr; 97 } 98 } 99 100 101 /*-------------------------------------------------------- */ 102 /* Context - Context stack */ 103 public class Context { 104 105 Stack<ContextVal> stack; 106 107 private boolean hasCP; 108 private boolean hasMethods; 109 private boolean hasInterfaces; 110 private boolean hasFields; 111 112 Context() { 113 stack = new Stack<>(); 114 init(); 115 } 116 117 boolean isConstantPool() { 118 return !stack.empty() && (stack.peek().tag == ContextTag.CONSTANTPOOL); 119 } 120 121 public void init() { 122 stack.removeAllElements(); 123 hasCP = false; 124 hasMethods = false; 125 hasInterfaces = false; 126 hasFields = false; 127 } 128 129 void update() { 130 if (stack.empty()) { 131 stack.push(new ContextVal(ContextTag.CLASS)); 132 return; 133 } 134 135 ContextVal currentCtx = stack.peek(); 136 switch (currentCtx.tag) { 137 case CLASS: 138 if (!hasCP) { 139 stack.push(new ContextVal(ContextTag.CONSTANTPOOL)); 140 hasCP = true; 141 } else if (!hasInterfaces) { 142 stack.push(new ContextVal(ContextTag.INTERFACES)); 143 hasInterfaces = true; 144 } else if (!hasFields) { 145 stack.push(new ContextVal(ContextTag.FIELDS)); 146 hasFields = true; 147 } else if (!hasMethods) { 148 stack.push(new ContextVal(ContextTag.METHODS)); 149 hasMethods = true; 150 } else { 151 // must be class attributes 152 currentCtx.compCount += 1; 153 stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); 154 } 155 break; 156 case INTERFACES: 157 currentCtx.compCount += 1; 158 stack.push(new ContextVal(ContextTag.INTERFACE, currentCtx)); 159 break; 160 case FIELDS: 161 currentCtx.compCount += 1; 162 stack.push(new ContextVal(ContextTag.FIELD, currentCtx)); 163 break; 164 case METHODS: 165 currentCtx.compCount += 1; 166 stack.push(new ContextVal(ContextTag.METHOD, currentCtx)); 167 break; 168 case FIELD: 169 case METHOD: 170 case ATTRIBUTE: 171 currentCtx.compCount += 1; 172 stack.push(new ContextVal(ContextTag.ATTRIBUTE, currentCtx)); 173 break; 174 default: 175 break; 176 } 177 } 178 179 void exit() { 180 if (!stack.isEmpty()) { 181 stack.pop(); 182 } 183 } 184 185 public String toString() { 186 if (stack.isEmpty()) { 187 return ""; 188 } 189 ContextVal currentCtx = stack.peek(); 190 String retval = currentCtx.tag.printval(); 191 switch (currentCtx.tag) { 192 case INTERFACE: 193 case METHOD: 194 case FIELD: 195 case ATTRIBUTE: 196 if (currentCtx.owner != null) { 197 retval += "[" + currentCtx.owner.compCount + "]"; 198 } 199 } 200 201 return retval; 202 } 203 } 204 205 206 /*-------------------------------------------------------- */ 207 /* Jcoder */ 208 /** 209 * Create a parser 210 */ 211 Jcoder(SourceFile sf, HashMap<String, String> macros) throws IOException { 212 scanner = new Scanner(sf, macros); 213 env = sf; 214 context = new Context(); 215 216 } 217 /*-------------------------------------------------------- */ 218 219 /** 220 * Expect a token, return its value, scan the next token or throw an exception. 221 */ 222 private void expect(Token t) throws SyntaxError, IOException { 223 if (scanner.token != t) { 224 env.traceln("expect:" + t + " instead of " + scanner.token); 225 switch (t) { 226 case IDENT: 227 env.error(scanner.pos, "identifier.expected"); 228 break; 229 default: 230 env.error(scanner.pos, "token.expected", t.toString()); 231 break; 232 } 233 throw new SyntaxError(); 234 } 235 scanner.scan(); 236 } 237 238 private void recoverField() throws SyntaxError, IOException { 239 while (true) { 240 switch (scanner.token) { 241 case LBRACE: 242 scanner.match(Token.LBRACE, Token.RBRACE); 243 scanner.scan(); 244 break; 245 246 case LPAREN: 247 scanner.match(Token.LPAREN, Token.RPAREN); 248 scanner.scan(); 249 break; 250 251 case LSQBRACKET: 252 scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); 253 scanner.scan(); 254 break; 255 256 case RBRACE: 257 case EOF: 258 case INTERFACE: 259 case CLASS: 260 // begin of something outside a class, panic more 261 throw new SyntaxError(); 262 263 default: 264 // don't know what to do, skip 265 scanner.scan(); 266 break; 267 } 268 } 269 } 270 271 /** 272 * Parse an array of struct. 273 */ 274 private void parseArray() throws IOException { 275 scanner.scan(); 276 int length0 = buf.length, pos0 = scanner.pos; 277 int num_expected; 278 if (scanner.token == Token.INTVAL) { 279 num_expected = scanner.intValue; 280 scanner.scan(); 281 } else { 282 num_expected = -1; 283 } 284 expect(Token.RSQBRACKET); 285 int numSize; 286 switch (scanner.token) { 287 case BYTEINDEX: 288 scanner.scan(); 289 numSize = 1; 290 break; 291 case SHORTINDEX: 292 scanner.scan(); 293 numSize = 2; 294 break; 295 case ZEROINDEX: 296 scanner.scan(); 297 numSize = 0; 298 break; 299 default: 300 numSize = 2; 301 } 302 303 // skip array size 304 if (numSize > 0) { 305 buf.append(num_expected, numSize); 306 } 307 308 int num_present = parseStruct(); 309 if (num_expected == -1) { 310 env.trace(" buf.writeAt(" + length0 + ", " + num_present + ", " + numSize + "); "); 311 // skip array size 312 if (numSize > 0) { 313 buf.writeAt(length0, num_present, numSize); 314 } 315 } else if ( num_expected != num_present) { 316 if (context.isConstantPool() && num_expected == num_present +1) return; 317 env.error(pos0, "warn.array.wronglength", num_expected, num_present); 318 } 319 } 320 321 /** 322 * Parse a byte array. 323 */ 324 private void parseByteArray() throws IOException { 325 scanner.scan(); 326 expect(Token.LSQBRACKET); 327 int length0 = buf.length, pos0 = scanner.pos; 328 int len_expected; 329 if (scanner.token == Token.INTVAL) { 330 len_expected = scanner.intValue; 331 scanner.scan(); 332 } else { 333 len_expected = -1; 334 } 335 expect(Token.RSQBRACKET); 336 int lenSize; 337 switch (scanner.token) { 338 case BYTEINDEX: 339 scanner.scan(); 340 lenSize = 1; 341 break; 342 case SHORTINDEX: 343 scanner.scan(); 344 lenSize = 2; 345 break; 346 case ZEROINDEX: 347 scanner.scan(); 348 lenSize = 0; 349 break; 350 default: 351 lenSize = 4; 352 } 353 354 // skip array size 355 if (lenSize > 0) { 356 buf.append(len_expected, lenSize); 357 } 358 int length1 = buf.length; 359 parseStruct(); 360 int len_present = buf.length - length1; 361 if (len_expected == -1) { 362 env.trace(" buf.writeAt(" + length0 + ", " + len_present + ", " + lenSize + "); "); 363 // skip array size 364 if (lenSize > 0) { 365 buf.writeAt(length0, len_present, lenSize); 366 } 367 } else if (len_expected != len_present) { 368 env.error(pos0, "warn.array.wronglength", len_expected, len_present); 369 } 370 } 371 372 /** 373 * Parse an Attribute. 374 */ 375 private void parseAttr() throws IOException { 376 scanner.scan(); 377 expect(Token.LPAREN); 378 int cpx; // index int const. pool 379 if (scanner.token == Token.INTVAL) { 380 cpx = scanner.intValue; 381 scanner.scan(); 382 383 /* } else if (token==STRINGVAL) { 384 Integer Val=(Integer)(CP_Strings.get(stringValue)); 385 if (Val == null) { 386 env.error(pos, "attrname.notfound", stringValue); 387 throw new SyntaxError(); 388 } 389 cpx=Val.intValue(); 390 */ } else { 391 env.error(scanner.pos, "attrname.expected"); 392 throw new SyntaxError(); 393 } 394 buf.append(cpx, 2); 395 int pos0 = scanner.pos, length0 = buf.length; 396 int len_expected; 397 if (scanner.token == Token.COMMA) { 398 scanner.scan(); 399 len_expected = scanner.intValue; 400 expect(Token.INTVAL); 401 } else { 402 len_expected = -1; 403 } 404 buf.append(len_expected, 4); 405 expect(Token.RPAREN); 406 parseStruct(); 407 int len_present = buf.length - (length0 + 4); 408 if (len_expected == -1) { 409 buf.writeAt(length0, len_present, 4); 410 } else if (len_expected != len_present) { 411 env.error(pos0, "warn.attr.wronglength", len_expected, len_present); 412 } 413 } // end parseAttr 414 415 /** 416 * Parse a Component of JavaCard .cap file. 417 */ 418 private void parseComp() throws IOException { 419 scanner.scan(); 420 expect(Token.LPAREN); 421 int tag = scanner.intValue; // index int const. pool 422 expect(Token.INTVAL); 423 buf.append(tag, 1); 424 int pos0 = scanner.pos, length0 = buf.length; 425 int len_expected; 426 if (scanner.token == Token.COMMA) { 427 scanner.scan(); 428 len_expected = scanner.intValue; 429 expect(Token.INTVAL); 430 } else { 431 len_expected = -1; 432 } 433 buf.append(len_expected, 2); 434 expect(Token.RPAREN); 435 parseStruct(); 436 int len_present = buf.length - (length0 + 2); 437 if (len_expected == -1) { 438 buf.writeAt(length0, len_present, 2); 439 } else if (len_expected != len_present) { 440 env.error(pos0, "warn.attr.wronglength", len_expected, len_present); 441 } 442 } // end parseComp 443 444 private void adjustDepth(boolean up) { 445 if (up) { 446 depth += 1; 447 context.update(); 448 scanner.setDebugCP(context.isConstantPool()); 449 } else { 450 depth -= 1; 451 context.exit(); 452 } 453 StringBuilder bldr = new StringBuilder(); 454 int tabAmt = 4; 455 int len = depth * tabAmt; 456 for (int i = 0; i < len; i++) { 457 bldr.append(" "); 458 } 459 tabStr = bldr.toString(); 460 } 461 462 /** 463 * Parse a structure. 464 */ 465 private int parseStruct() throws IOException { 466 adjustDepth(true); 467 env.traceln(" "); 468 env.traceln(tabStr + "MapStruct { <" + context + "> "); 469 expect(Token.LBRACE); 470 int num = 0; 471 int addElem = 0; 472 while (true) { 473 try { 474 switch (scanner.token) { 475 case COMMA: // ignored 476 scanner.scan(); 477 break; 478 case SEMICOLON: 479 num++; 480 addElem = 0; 481 scanner.scan(); 482 break; 483 case CLASS: 484 scanner.addConstDebug(ConstType.CONSTANT_CLASS); 485 env.trace("class "); 486 scanner.longValue = ConstType.CONSTANT_CLASS.value(); 487 scanner.intSize = 1; 488 case INTVAL: 489 env.trace("int [" + scanner.longValue + "] "); 490 buf.append(scanner.longValue, scanner.intSize); 491 scanner.scan(); 492 addElem = 1; 493 break; 494 case STRINGVAL: 495 scanner.scan(); 496 scanner.addConstDebug(ConstType.CONSTANT_UTF8); 497 env.trace("UTF8 [\"" + scanner.stringValue + "\"] "); 498 bufstream.writeUTF(scanner.stringValue); 499 addElem = 1; 500 break; 501 case LONGSTRINGVAL: 502 scanner.scan(); 503 env.traceln("LongString [\"" + Arrays.toString(scanner.longStringValue.data) + "\"] "); 504 buf.write(scanner.longStringValue.data, 0, scanner.longStringValue.length); 505 addElem = 1; 506 break; 507 case LBRACE: 508 parseStruct(); 509 addElem = 1; 510 break; 511 case LSQBRACKET: 512 parseArray(); 513 addElem = 1; 514 break; 515 case BYTES: 516 env.trace("bytes "); 517 parseByteArray(); 518 addElem = 1; 519 break; 520 case ATTR: 521 env.trace("attr "); 522 parseAttr(); 523 addElem = 1; 524 break; 525 case COMP: 526 env.trace("comp "); 527 parseComp(); 528 addElem = 1; 529 break; 530 case RBRACE: 531 scanner.scan(); 532 env.traceln(" "); 533 env.traceln(tabStr + "} // MapStruct <" + context + "> ["); 534 adjustDepth(false); 535 return num + addElem; 536 default: 537 env.traceln("unexp token=" + scanner.token); 538 env.traceln(" scanner.stringval = \"" + scanner.stringValue + "\""); 539 env.error(scanner.pos, "element.expected"); 540 throw new SyntaxError(); 541 } 542 } catch (SyntaxError e) { 543 recoverField(); 544 } 545 } 546 } // end parseStruct 547 548 /** 549 * Recover after a syntax error in the file. This involves discarding tokens until an 550 * EOF or a possible legal continuation is encountered. 551 */ 552 private void recoverFile() throws IOException { 553 while (true) { 554 switch (scanner.token) { 555 case CLASS: 556 case INTERFACE: 557 // Start of a new source file statement, continue 558 return; 559 560 case LBRACE: 561 scanner.match(Token.LBRACE, Token.RBRACE); 562 scanner.scan(); 563 break; 564 565 case LPAREN: 566 scanner.match(Token.LPAREN, Token.RPAREN); 567 scanner.scan(); 568 break; 569 570 case LSQBRACKET: 571 scanner.match(Token.LSQBRACKET, Token.RSQBRACKET); 572 scanner.scan(); 573 break; 574 575 case EOF: 576 return; 577 578 default: 579 // Don't know what to do, skip 580 scanner.scan(); 581 break; 582 } 583 } 584 } 585 586 /** 587 * Parse module declaration 588 */ 589 private void parseModule() throws IOException { 590 // skip module name as a redundant element 591 scanner.skipTill(Scanner.LBRACE); 592 buf = new ByteBuffer(); 593 bufstream = new DataOutputStream(buf); 594 buf.myname = "module-info.class"; 595 scanner.scan(); 596 env.traceln("starting " + buf.myname); 597 // Parse the clause 598 parseClause(); 599 env.traceln("ending " + buf.myname); 600 } 601 602 /** 603 * Parse a class or interface declaration. 604 */ 605 private void parseClass(Token prev) throws IOException { 606 scanner.scan(); 607 buf = new ByteBuffer(); 608 bufstream = new DataOutputStream(buf); 609 // Parse the class name 610 switch (scanner.token) { 611 case STRINGVAL: 612 buf.myname = scanner.stringValue; 613 break; 614 case BYTEINDEX: 615 case SHORTINDEX: 616 case ATTR: 617 case BYTES: 618 case MACRO: 619 case COMP: 620 case FILE: 621 case IDENT: 622 if (prev == Token.FILE) { 623 buf.myname = scanner.stringValue; 624 } else { 625 buf.myname = scanner.stringValue + ".class"; 626 } 627 break; 628 default: 629 env.error(scanner.prevPos, "name.expected"); 630 throw new SyntaxError(); 631 } 632 scanner.scan(); 633 env.traceln("starting class " + buf.myname); 634 // Parse the clause 635 parseClause(); 636 env.traceln("ending class " + buf.myname); 637 638 } // end parseClass 639 640 private void parseClause() throws IOException { 641 switch (scanner.token) { 642 case LBRACE: 643 parseStruct(); 644 break; 645 case LSQBRACKET: 646 parseArray(); 647 break; 648 case BYTES: 649 parseByteArray(); 650 break; 651 case ATTR: 652 parseAttr(); 653 break; 654 case COMP: 655 parseComp(); 656 break; 657 default: 658 env.error(scanner.pos, "struct.expected"); 659 } 660 } 661 662 /** 663 * Parse an Jcoder file. 664 */ 665 void parseFile() { 666 env.traceln("PARSER"); 667 context.init(); 668 try { 669 while (scanner.token != Token.EOF) { 670 try { 671 switch (scanner.token) { 672 case CLASS: 673 case MODULE: 674 case INTERFACE: 675 case FILE: 676 Token t = scanner.token; 677 if ( t == Token.MODULE) { 678 parseModule(); 679 } else { 680 parseClass(t); 681 } 682 // End of the class,interface or module 683 env.flushErrors(); 684 Classes.add(buf); 685 break; 686 case SEMICOLON: 687 // Bogus semi colon 688 scanner.scan(); 689 break; 690 691 case EOF: 692 // The end 693 return; 694 695 default: 696 env.traceln("unexpected token=" + scanner.token.toString()); 697 env.error(scanner.pos, "toplevel.expected"); 698 throw new SyntaxError(); 699 } 700 } catch (SyntaxError e) { 701 String msg = e.getMessage(); 702 env.traceln("SyntaxError " + (msg == null ? "" : msg)); 703 if( env.debugInfoFlag ) { 704 e.printStackTrace(); 705 } 706 recoverFile(); 707 } 708 } 709 } catch (IOException e) { 710 env.error(scanner.pos, "io.exception", env.getInputFileName()); 711 } 712 } //end parseFile 713 714 /*---------------------------------------------*/ 715 private static char fileSeparator; //=System.getProperty("file.separator"); 716 717 /** 718 * write to the directory passed with -d option 719 */ 720 public void write(ByteBuffer cls, File destdir) throws IOException { 721 String myname = cls.myname; 722 if (myname == null) { 723 env.error("cannot.write", null); 724 return; 725 } 726 727 env.traceln("writing " + myname); 728 File outfile; 729 if (destdir == null) { 730 int startofname = myname.lastIndexOf('/'); 731 if (startofname != -1) { 732 myname = myname.substring(startofname + 1); 733 } 734 outfile = new File(myname); 735 } else { 736 env.traceln("writing -d " + destdir.getPath()); 737 if (fileSeparator == 0) { 738 fileSeparator = System.getProperty("file.separator").charAt(0); 739 } 740 if (fileSeparator != '/') { 741 myname = myname.replace('/', fileSeparator); 742 } 743 outfile = new File(destdir, myname); 744 File outdir = new File(outfile.getParent()); 745 if (!outdir.exists() && !outdir.mkdirs()) { 746 env.error("cannot.write", outdir.getPath()); 747 return; 748 } 749 } 750 751 BufferedOutputStream out = new BufferedOutputStream(new FileOutputStream(outfile)); 752 out.write(cls.data, 0, cls.length); 753 try { 754 out.close(); 755 } catch (IOException ignored) { } 756 } 757 758 /** 759 * Writes the classes 760 */ 761 public void write(File destdir) throws IOException { 762 for (ByteBuffer cls : Classes) { 763 write(cls, destdir); 764 } 765 } // end write() 766 } // end Jcoder