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