1 /* 2 * Copyright (c) 2017, 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 24 package jdk.experimental.bytecode; 25 26 import java.util.ArrayList; 27 import java.util.HashMap; 28 import java.util.Iterator; 29 import java.util.LinkedHashMap; 30 import java.util.LinkedList; 31 import java.util.List; 32 import java.util.Map; 33 import java.util.TreeMap; 34 import java.util.function.Consumer; 35 36 public class MacroCodeBuilder<S, T, E, C extends MacroCodeBuilder<S, T, E, C>> extends CodeBuilder<S, T, E, C> { 37 38 JumpMode jumpMode = JumpMode.NARROW; 39 40 Map<CharSequence, Integer> labels = new HashMap<>(); 41 List<PendingJump> pendingJumps = new LinkedList<>(); 42 43 class PendingJump { 44 CharSequence label; 45 int pc; 46 47 PendingJump(CharSequence label, int pc) { 48 this.label = label; 49 this.pc = pc; 50 } 51 52 boolean resolve(CharSequence label, int offset) { 53 if (this.label.equals(label)) { 54 //patch offset 55 code.withOffset(pc + 1, buf -> emitOffset(buf, jumpMode, offset - pc)); 56 return true; 57 } else { 58 return false; 59 } 60 } 61 } 62 63 public enum InvocationKind { 64 INVOKESTATIC, 65 INVOKEVIRTUAL, 66 INVOKESPECIAL, 67 INVOKEINTERFACE; 68 } 69 70 public enum FieldAccessKind { 71 STATIC, 72 INSTANCE; 73 } 74 75 public enum CondKind { 76 EQ(0), 77 NE(1), 78 LT(2), 79 GE(3), 80 GT(4), 81 LE(5); 82 83 int offset; 84 85 CondKind(int offset) { 86 this.offset = offset; 87 } 88 89 public CondKind negate() { 90 switch (this) { 91 case EQ: 92 return NE; 93 case NE: 94 return EQ; 95 case LT: 96 return GE; 97 case GE: 98 return LT; 99 case GT: 100 return LE; 101 case LE: 102 return GT; 103 default: 104 throw new IllegalStateException("Unknown cond"); 105 } 106 } 107 } 108 109 static class WideJumpException extends RuntimeException { 110 static final long serialVersionUID = 42L; 111 } 112 113 public MacroCodeBuilder(MethodBuilder<S, T, E> methodBuilder) { 114 super(methodBuilder); 115 } 116 117 public C load(TypeTag type, int n) { 118 switch (n) { 119 case 0: 120 return emitOp(Opcode.ILOAD_0.at(type, 4)); 121 case 1: 122 return emitOp(Opcode.ILOAD_1.at(type, 4)); 123 case 2: 124 return emitOp(Opcode.ILOAD_2.at(type, 4)); 125 case 3: 126 return emitOp(Opcode.ILOAD_3.at(type, 4)); 127 default: 128 return emitWideIfNeeded(Opcode.ILOAD.at(type), n); 129 } 130 } 131 132 public C store(TypeTag type, int n) { 133 switch (n) { 134 case 0: 135 return emitOp(Opcode.ISTORE_0.at(type, 4)); 136 case 1: 137 return emitOp(Opcode.ISTORE_1.at(type, 4)); 138 case 2: 139 return emitOp(Opcode.ISTORE_2.at(type, 4)); 140 case 3: 141 return emitOp(Opcode.ISTORE_3.at(type, 4)); 142 default: 143 return emitWideIfNeeded(Opcode.ISTORE.at(type), n); 144 } 145 } 146 147 public C arrayload(TypeTag type) { 148 return emitOp(Opcode.IALOAD.at(type)); 149 } 150 151 public C arraystore(TypeTag type, int n) { 152 return emitOp(Opcode.IASTORE.at(type)); 153 } 154 155 public C const_(int i) { 156 switch (i) { 157 case -1: 158 return iconst_m1(); 159 case 0: 160 return iconst_0(); 161 case 1: 162 return iconst_1(); 163 case 2: 164 return iconst_2(); 165 case 3: 166 return iconst_3(); 167 case 4: 168 return iconst_4(); 169 case 5: 170 return iconst_5(); 171 default: 172 if (i > 0 && i <= Byte.MAX_VALUE) { 173 return bipush(i); 174 } else if (i >= Short.MIN_VALUE && i <= Short.MAX_VALUE) { 175 return sipush(i); 176 } else { 177 return ldc(i); 178 } 179 } 180 } 181 182 public C const_(long l) { 183 if (l == 0) { 184 return lconst_0(); 185 } else if (l == 1) { 186 return lconst_1(); 187 } else { 188 return ldc(l); 189 } 190 } 191 192 public C const_(float f) { 193 if (f == 0) { 194 return fconst_0(); 195 } else if (f == 1) { 196 return fconst_1(); 197 } else if (f == 2) { 198 return fconst_2(); 199 } else { 200 return ldc(f); 201 } 202 } 203 204 public C const_(double d) { 205 if (d == 0) { 206 return dconst_0(); 207 } else if (d == 1) { 208 return dconst_1(); 209 } else { 210 return ldc(d); 211 } 212 } 213 214 public C getfield(FieldAccessKind fak, S owner, CharSequence name, T type) { 215 switch (fak) { 216 case INSTANCE: 217 return getfield(owner, name, type); 218 case STATIC: 219 return getstatic(owner, name, type); 220 default: 221 throw new IllegalStateException(); 222 } 223 } 224 225 public C putfield(FieldAccessKind fak, S owner, CharSequence name, T type) { 226 switch (fak) { 227 case INSTANCE: 228 return putfield(owner, name, type); 229 case STATIC: 230 return putstatic(owner, name, type); 231 default: 232 throw new IllegalStateException(); 233 } 234 } 235 236 public C invoke(InvocationKind ik, S owner, CharSequence name, T type, boolean isInterface) { 237 switch (ik) { 238 case INVOKESTATIC: 239 return invokestatic(owner, name, type, isInterface); 240 case INVOKEVIRTUAL: 241 return invokevirtual(owner, name, type, isInterface); 242 case INVOKESPECIAL: 243 return invokespecial(owner, name, type, isInterface); 244 case INVOKEINTERFACE: 245 if (!isInterface) throw new AssertionError(); 246 return invokeinterface(owner, name, type); 247 default: 248 throw new IllegalStateException(); 249 } 250 } 251 252 public C add(TypeTag type) { 253 return emitOp(Opcode.IADD.at(type)); 254 } 255 256 public C sub(TypeTag type) { 257 return emitOp(Opcode.ISUB.at(type)); 258 } 259 260 public C mul(TypeTag type) { 261 return emitOp(Opcode.IMUL.at(type)); 262 } 263 264 public C div(TypeTag type) { 265 return emitOp(Opcode.IDIV.at(type)); 266 } 267 268 public C rem(TypeTag type) { 269 return emitOp(Opcode.IREM.at(type)); 270 } 271 272 public C neg(TypeTag type) { 273 return emitOp(Opcode.INEG.at(type)); 274 } 275 276 public C shl(TypeTag type) { 277 return emitOp(Opcode.ISHL.at(type)); 278 } 279 280 public C shr(TypeTag type) { 281 return emitOp(Opcode.ISHR.at(type)); 282 } 283 284 public C ushr(TypeTag type) { 285 return emitOp(Opcode.ISHR.at(type)); 286 } 287 288 public C and(TypeTag type) { 289 return emitOp(Opcode.IAND.at(type)); 290 } 291 292 public C or(TypeTag type) { 293 return emitOp(Opcode.IOR.at(type)); 294 } 295 296 public C xor(TypeTag type) { 297 return emitOp(Opcode.IXOR.at(type)); 298 } 299 300 public C return_(TypeTag type) { 301 switch (type) { 302 case V: 303 case Q: 304 return return_(); 305 default: 306 return emitOp(Opcode.IRETURN.at(type)); 307 } 308 } 309 310 public C conv(TypeTag from, TypeTag to) { 311 switch (from) { 312 case B: 313 case C: 314 case S: 315 switch (to) { 316 case J: 317 return i2l(); 318 case F: 319 return i2f(); 320 case D: 321 return i2d(); 322 } 323 break; 324 case I: 325 switch (to) { 326 case J: 327 return i2l(); 328 case F: 329 return i2f(); 330 case D: 331 return i2d(); 332 case B: 333 return i2b(); 334 case C: 335 return i2c(); 336 case S: 337 return i2s(); 338 } 339 break; 340 case J: 341 switch (to) { 342 case I: 343 return l2i(); 344 case F: 345 return l2f(); 346 case D: 347 return l2d(); 348 } 349 break; 350 case F: 351 switch (to) { 352 case I: 353 return f2i(); 354 case J: 355 return f2l(); 356 case D: 357 return f2d(); 358 } 359 break; 360 case D: 361 switch (to) { 362 case I: 363 return d2i(); 364 case J: 365 return d2l(); 366 case F: 367 return d2f(); 368 } 369 break; 370 } 371 //no conversion is necessary - do nothing! 372 return thisBuilder(); 373 } 374 375 public C if_null(CharSequence label) { 376 return emitCondJump(Opcode.IF_NULL, Opcode.IF_NONNULL, label); 377 } 378 379 public C if_nonnull(CharSequence label) { 380 return emitCondJump(Opcode.IF_NONNULL, Opcode.IF_NULL, label); 381 } 382 383 public C ifcmp(TypeTag type, CondKind cond, CharSequence label) { 384 switch (type) { 385 case I: 386 return emitCondJump(Opcode.IF_ICMPEQ, cond, label); 387 case A: 388 return emitCondJump(Opcode.IF_ACMPEQ, cond, label); 389 case J: 390 return lcmp().emitCondJump(Opcode.IFEQ, cond, label); 391 case D: 392 return dcmpg().emitCondJump(Opcode.IFEQ, cond, label); 393 case F: 394 return fcmpg().emitCondJump(Opcode.IFEQ, cond, label); 395 default: 396 throw new IllegalArgumentException("Bad cmp type"); 397 } 398 } 399 400 public C goto_(CharSequence label) { 401 emitOp(jumpMode == JumpMode.NARROW ? Opcode.GOTO_ : Opcode.GOTO_W); 402 emitOffset(code, jumpMode, labelOffset(label)); 403 return thisBuilder(); 404 } 405 406 protected int labelOffset(CharSequence label) { 407 int pc = code.offset - 1; 408 Integer labelPc = labels.get(label); 409 if (labelPc == null) { 410 addPendingJump(label, pc); 411 } 412 return labelPc == null ? 0 : (labelPc - pc); 413 } 414 415 public C label(CharSequence s) { 416 int pc = code.offset; 417 Object old = labels.put(s, pc); 418 if (old != null) { 419 throw new IllegalStateException("label already exists"); 420 } 421 resolveJumps(s, pc); 422 return thisBuilder(); 423 } 424 425 //FIXME: address this jumpy mess - i.e. offset and state update work against each other! 426 public C emitCondJump(Opcode opcode, CondKind ck, CharSequence label) { 427 return emitCondJump(opcode.at(ck), opcode.at(ck.negate()), label); 428 } 429 430 public C emitCondJump(Opcode pos, Opcode neg, CharSequence label) { 431 if (jumpMode == JumpMode.NARROW) { 432 emitOp(pos); 433 emitOffset(code, jumpMode, labelOffset(label)); 434 } else { 435 emitOp(neg); 436 emitOffset(code, JumpMode.NARROW, 8); 437 goto_w(labelOffset(label)); 438 } 439 return thisBuilder(); 440 } 441 442 void addPendingJump(CharSequence label, int pc) { 443 pendingJumps.add(new PendingJump(label, pc)); 444 } 445 446 void resolveJumps(CharSequence label, int pc) { 447 Iterator<PendingJump> jumpsIt = pendingJumps.iterator(); 448 while (jumpsIt.hasNext()) { 449 PendingJump jump = jumpsIt.next(); 450 if (jump.resolve(label, pc)) { 451 jumpsIt.remove(); 452 } 453 } 454 } 455 456 @Override 457 protected void emitOffset(GrowableByteBuffer buf, JumpMode jumpMode, int offset) { 458 if (jumpMode == JumpMode.NARROW && (offset < Short.MIN_VALUE || offset > Short.MAX_VALUE)) { 459 throw new WideJumpException(); 460 } 461 super.emitOffset(buf, jumpMode, offset); 462 } 463 464 public C jsr(CharSequence label) { 465 emitOp(jumpMode == JumpMode.NARROW ? Opcode.JSR : Opcode.JSR_W); 466 emitOffset(code, jumpMode, labelOffset(label)); 467 return thisBuilder(); 468 } 469 470 @SuppressWarnings("unchecked") 471 public C withTry(Consumer<? super C> tryBlock, Consumer<? super CatchBuilder> catchBlocks) { 472 int start = code.offset; 473 tryBlock.accept((C) this); 474 int end = code.offset; 475 CatchBuilder catchBuilder = makeCatchBuilder(start, end); 476 catchBlocks.accept(catchBuilder); 477 catchBuilder.build(); 478 return thisBuilder(); 479 } 480 481 void clear() { 482 code.offset = 0; 483 catchers.offset = 0; 484 ncatchers = 0; 485 labels.clear(); 486 pendingJumps = null; 487 } 488 489 protected CatchBuilder makeCatchBuilder(int start, int end) { 490 return new CatchBuilder(start, end); 491 } 492 493 public class CatchBuilder { 494 int start, end; 495 496 String endLabel = labelName(); 497 498 Map<S, Consumer<? super C>> catchers = new LinkedHashMap<>(); 499 public Consumer<? super C> finalizer; 500 List<Integer> pendingGaps = new ArrayList<>(); 501 502 public CatchBuilder(int start, int end) { 503 this.start = start; 504 this.end = end; 505 } 506 507 public CatchBuilder withCatch(S exc, Consumer<? super C> catcher) { 508 catchers.put(exc, catcher); 509 return this; 510 } 511 512 public CatchBuilder withFinally(Consumer<? super C> finalizer) { 513 this.finalizer = finalizer; 514 return this; 515 } 516 517 @SuppressWarnings("unchecked") 518 void build() { 519 if (finalizer != null) { 520 finalizer.accept((C) MacroCodeBuilder.this); 521 } 522 goto_(endLabel); 523 for (Map.Entry<S, Consumer<? super C>> catcher_entry : catchers.entrySet()) { 524 emitCatch(catcher_entry.getKey(), catcher_entry.getValue()); 525 } 526 if (finalizer != null) { 527 emitFinalizer(); 528 } 529 resolveJumps(endLabel, code.offset); 530 } 531 532 @SuppressWarnings("unchecked") 533 protected void emitCatch(S exc, Consumer<? super C> catcher) { 534 int offset = code.offset; 535 MacroCodeBuilder.this.withCatch(exc, start, end, offset); 536 catcher.accept((C) MacroCodeBuilder.this); 537 if (finalizer != null) { 538 int startFinalizer = code.offset; 539 finalizer.accept((C) MacroCodeBuilder.this); 540 pendingGaps.add(startFinalizer); 541 pendingGaps.add(code.offset); 542 } 543 goto_(endLabel); 544 } 545 546 @SuppressWarnings("unchecked") 547 protected void emitFinalizer() { 548 int offset = code.offset; 549 pop(); 550 for (int i = 0; i < pendingGaps.size(); i += 2) { 551 MacroCodeBuilder.this.withCatch(null, pendingGaps.get(i), pendingGaps.get(i + 1), offset); 552 } 553 MacroCodeBuilder.this.withCatch(null, start, end, offset); 554 finalizer.accept((C) MacroCodeBuilder.this); 555 } 556 557 // @SuppressWarnings("unchecked") 558 // CatchBuilder withCatch(S exc, Consumer<? super C> catcher) { 559 // int offset = code.offset; 560 // MacroCodeBuilder.this.withCatch(exc, start, end, offset); 561 // catcher.accept((C)MacroCodeBuilder.this); 562 // return this; 563 // } 564 // 565 // @SuppressWarnings("unchecked") 566 // CatchBuilder withFinally(Consumer<? super C> catcher) { 567 // int offset = code.offset; 568 // MacroCodeBuilder.this.withCatch(null, start, end, offset); 569 // catcher.accept((C)MacroCodeBuilder.this); 570 // return this; 571 // } 572 } 573 574 @SuppressWarnings("unchecked") 575 public C switch_(Consumer<? super SwitchBuilder> consumer) { 576 int start = code.offset; 577 SwitchBuilder sb = makeSwitchBuilder(); 578 consumer.accept(sb); 579 int nlabels = sb.cases.size(); 580 switch (sb.switchCode()) { 581 case LOOKUPSWITCH: { 582 int[] lookupOffsets = new int[nlabels * 2]; 583 int i = 0; 584 for (Integer v : sb.cases.keySet()) { 585 lookupOffsets[i] = v; 586 i += 2; 587 } 588 lookupswitch(0, lookupOffsets); 589 //backpatch lookup 590 int curr = code.offset - (8 * nlabels) - 8; 591 int defaultOffset = code.offset - start; 592 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset)); 593 sb.defaultCase.accept((C) this); 594 curr += 12; 595 for (Consumer<? super C> case_ : sb.cases.values()) { 596 int offset = code.offset; 597 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset - start)); 598 case_.accept((C) this); 599 curr += 8; 600 } 601 break; 602 } 603 case TABLESWITCH: { 604 int[] tableOffsets = new int[sb.hi - sb.lo + 1]; 605 tableswitch(sb.lo, sb.hi, 0, tableOffsets); 606 //backpatch table 607 int curr = code.offset - (4 * tableOffsets.length) - 12; 608 int defaultOffset = code.offset - start; 609 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, defaultOffset)); 610 sb.defaultCase.accept((C) this); 611 curr += 12; 612 int lastCasePc = -1; 613 for (int i = sb.lo; i <= sb.hi; i++) { 614 Consumer<? super C> case_ = sb.cases.get(i); 615 if (case_ != null) { 616 lastCasePc = code.offset; 617 case_.accept((C) this); 618 } 619 int offset = lastCasePc - start; 620 code.withOffset(curr, buf -> emitOffset(buf, JumpMode.WIDE, offset)); 621 curr += 4; 622 } 623 } 624 } 625 resolveJumps(sb.endLabel, code.offset); 626 return thisBuilder(); 627 } 628 629 private static int labelCount = 0; 630 631 String labelName() { 632 return "label" + labelCount++; 633 } 634 635 protected SwitchBuilder makeSwitchBuilder() { 636 return new SwitchBuilder(); 637 } 638 639 public class SwitchBuilder { 640 Map<Integer, Consumer<? super C>> cases = new TreeMap<>(); 641 int lo = Integer.MAX_VALUE; 642 int hi = Integer.MIN_VALUE; 643 String endLabel = labelName(); 644 645 public Consumer<? super C> defaultCase; 646 647 @SuppressWarnings("unchecked") 648 public SwitchBuilder withCase(int value, Consumer<? super C> case_, boolean fallthrough) { 649 if (value > hi) { 650 hi = value; 651 } 652 if (value < lo) { 653 lo = value; 654 } 655 if (!fallthrough) { 656 Consumer<? super C> prevCase = case_; 657 case_ = C -> { 658 prevCase.accept(C); 659 C.goto_(endLabel); 660 }; 661 } 662 cases.put(value, case_); 663 return this; 664 } 665 666 @SuppressWarnings("unchecked") 667 public SwitchBuilder withDefault(Consumer<? super C> defaultCase) { 668 if (this.defaultCase != null) { 669 throw new IllegalStateException("default already set"); 670 } 671 this.defaultCase = defaultCase; 672 return this; 673 } 674 675 Opcode switchCode() { 676 int nlabels = cases.size(); 677 // Determine whether to issue a tableswitch or a lookupswitch 678 // instruction. 679 long table_space_cost = 4 + ((long) hi - lo + 1); // words 680 long lookup_space_cost = 3 + 2 * (long) nlabels; 681 return 682 nlabels > 0 && 683 table_space_cost <= lookup_space_cost 684 ? 685 Opcode.TABLESWITCH : Opcode.LOOKUPSWITCH; 686 } 687 } 688 }