8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import jdk.experimental.bytecode.AnnotationsBuilder;
29 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind;
30 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind;
31 import jdk.experimental.bytecode.MethodBuilder;
32 import jdk.experimental.bytecode.TypeTag;
33 import jdk.experimental.value.MethodHandleBuilder;
34 import sun.invoke.util.VerifyType;
35 import sun.invoke.util.Wrapper;
36 import valhalla.shady.MinimalValueTypes_1_0;
37
38 import java.lang.invoke.LambdaForm.BasicType;
39 import java.lang.invoke.LambdaForm.Name;
40 import java.lang.invoke.MethodHandles.Lookup;
41
42 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE;
43 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
44 import static java.lang.invoke.LambdaForm.BasicType.basicType;
45 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField;
46 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
47 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface;
48 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial;
49 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
50 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
51 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField;
52 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
53 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL;
54 import static java.lang.invoke.MethodHandleStatics.newInternalError;
55
56 /**
57 * Utility class for spinning classfiles associated with lambda forms.
58 */
59 class LambdaFormBuilder extends MethodHandleBuilder {
60
61 private static final String OBJ = "java/lang/Object";
62 private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$Value$";
63 private static final String DEFAULT_CLASS = "MH";
64 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
65
66 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
67 String invokerName = form.lambdaName();
68 int p = invokerName.indexOf('.');
69 boolean overrideNames = p != -1;
70 String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName;
71 String className = overrideNames ?
72 CLASS_PREFIX + invokerName.substring(0, p) :
73 CLASS_PREFIX + DEFAULT_CLASS;
74 if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) {
75 // When DUMP_CLASS_FILES is true methodName will have a unique id
76 className = className + "_" + methodName;
77 }
78 return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(),
79 M -> new LambdaFormCodeBuilder(form, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType),
80 C -> new LambdaFormBuilder(C, form, invokerType).generateLambdaFormBody());
81 }
82
83 LambdaFormCodeBuilder builder;
84 LambdaForm lambdaForm;
85 MethodType invokerType;
103 BasicType type = lform.names[i].type();
104 index += type.basicTypeSlots();
105 }
106 }
107 return localsMap;
108 }
109
110 void generateLambdaFormBody() {
111 // iterate over the form's names, generating bytecode instructions for each
112 // start iterating at the first name following the arguments
113 Name onStack = null;
114 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
115 Name name = lambdaForm.names[i];
116
117 if (onStack != null && onStack.type != V_TYPE) {
118 // non-void: actually assign
119 builder.store(fromBasicType(onStack.type), localsMap[onStack.index()]);
120 }
121 onStack = name; // unless otherwise modified below
122 MemberName member = name.function.member();
123 if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) {
124 emitStaticInvoke(member, name);
125 } else {
126 emitInvoke(name);
127 }
128 }
129 emitReturn(onStack);
130 }
131
132 /**
133 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
134 */
135 private void emitReturn(Name onStack) {
136 // return statement
137 Class<?> rclass = invokerType.returnType();
138 BasicType rtype = lambdaForm.returnType();
139 assert(rtype == basicType(rclass)); // must agree
140 if (rtype == V_TYPE) {
141 // void
142 builder.return_();
243 // invocation
244 if (member.isMethod()) {
245 mtype = member.getMethodType().toMethodDescriptorString();
246 builder.invoke(invKindFromRefKind(refKind), defc, mname, mtype,
247 member.getDeclaringClass().isInterface());
248 } else {
249 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
250 builder.getfield(fieldKindFromRefKind(refKind), defc, mname, mtype);
251 }
252 // Issue a type assertion for the result, so we can avoid casts later.
253 if (name.type == L_TYPE) {
254 Class<?> rtype = member.getInvocationType().returnType();
255 assert(!rtype.isPrimitive());
256 }
257 }
258
259 /**
260 * Emit an invoke for the given name.
261 */
262 void emitInvoke(Name name) {
263 //assert(!isLinkerMethodInvoke(name)); // should use the static path for these
264 if (true) {
265 // push receiver
266 MethodHandle target = name.function.resolvedHandle();
267 assert(target != null) : name.exprString();
268 builder.ldc(target);
269 emitReferenceCast(MethodHandle.class, target);
270 }
271
272 // push arguments
273 emitPushArguments(name);
274
275 // invocation
276 MethodType type = name.function.methodType();
277 builder.invokevirtual(MethodHandle.class, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
278 }
279
280 private void emitPushArguments(Name args) {
281 emitPushArguments(args, 0);
282 }
283
308 builder.ldc(arg);
309 emitImplicitConversion(L_TYPE, ptype, arg);
310 }
311 }
312 }
313
314 TypeTag fromBasicType(BasicType type) {
315 switch (type) {
316 case I_TYPE: return TypeTag.I;
317 case J_TYPE: return TypeTag.J;
318 case F_TYPE: return TypeTag.F;
319 case D_TYPE: return TypeTag.D;
320 case L_TYPE: return TypeTag.A;
321 case V_TYPE: return TypeTag.V;
322 case Q_TYPE: return TypeTag.Q;
323 default:
324 throw new InternalError("unknown type: " + type);
325 }
326 }
327
328 InvocationKind invKindFromRefKind(int refKind) {
329 switch (refKind) {
330 case REF_invokeVirtual: return InvocationKind.INVOKEVIRTUAL;
331 case REF_invokeStatic: return InvocationKind.INVOKESTATIC;
332 case REF_invokeSpecial: return InvocationKind.INVOKESPECIAL;
333 case REF_invokeInterface: return InvocationKind.INVOKEINTERFACE;
334 }
335 throw new InternalError("refKind="+refKind);
336 }
337
338 FieldAccessKind fieldKindFromRefKind(int refKind) {
339 switch (refKind) {
340 case REF_getField:
341 case REF_putField: return FieldAccessKind.INSTANCE;
342 case REF_getStatic:
343 case REF_putStatic: return FieldAccessKind.STATIC;
344 }
345 throw new InternalError("refKind="+refKind);
346 }
347
348 static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder {
349 public LambdaFormCodeBuilder(LambdaForm form, MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
350 super(methodBuilder);
351 if (form.forceInline) {
352 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;");
353 }
354 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;")
355 .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;");
356 }
357
358 Class<?> typeOfLocal(int index) {
359 return typeHelper.symbol(state.locals.get(index));
360 }
361 }
362 }
|
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 package java.lang.invoke;
27
28 import jdk.experimental.bytecode.*;
29 import jdk.experimental.bytecode.MacroCodeBuilder.FieldAccessKind;
30 import jdk.experimental.bytecode.MacroCodeBuilder.InvocationKind;
31 import jdk.experimental.value.MethodHandleBuilder;
32 import sun.invoke.util.VerifyType;
33 import sun.invoke.util.Wrapper;
34 import valhalla.shady.MinimalValueTypes_1_0;
35
36 import java.lang.invoke.LambdaForm.BasicType;
37 import java.lang.invoke.LambdaForm.Name;
38 import java.lang.invoke.MethodHandles.Lookup;
39 import java.util.Arrays;
40 import java.util.stream.Stream;
41
42 import static java.lang.invoke.LambdaForm.BasicType.L_TYPE;
43 import static java.lang.invoke.LambdaForm.BasicType.V_TYPE;
44 import static java.lang.invoke.LambdaForm.BasicType.basicType;
45 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getField;
46 import static java.lang.invoke.MethodHandleNatives.Constants.REF_getStatic;
47 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeInterface;
48 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeSpecial;
49 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeStatic;
50 import static java.lang.invoke.MethodHandleNatives.Constants.REF_invokeVirtual;
51 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putField;
52 import static java.lang.invoke.MethodHandleNatives.Constants.REF_putStatic;
53 import static java.lang.invoke.MethodHandleStatics.PROFILE_GWT;
54 import static java.lang.invoke.MethodHandleStatics.PROFILE_LEVEL;
55 import static java.lang.invoke.MethodHandleStatics.newInternalError;
56 import static jdk.experimental.bytecode.MacroCodeBuilder.CondKind.NE;
57
58 /**
59 * Utility class for spinning classfiles associated with lambda forms.
60 */
61 class LambdaFormBuilder extends MethodHandleBuilder {
62
63 private static final String OBJ = "java/lang/Object";
64 private static final String CLASS_PREFIX = "java/lang/invoke/LambdaForm$Value$";
65 private static final String DEFAULT_CLASS = "MH";
66 private static final String LL_SIG = "(L" + OBJ + ";)L" + OBJ + ";";
67
68 private static final String MH = "java/lang/invoke/MethodHandle";
69 private static final String MHARY2 = "[[L" + MH + ";";
70
71 static MemberName generateCustomizedCode(LambdaForm form, MethodType invokerType) {
72 String invokerName = form.lambdaName();
73 int p = invokerName.indexOf('.');
74 boolean overrideNames = p != -1;
75 String methodName = overrideNames ? invokerName.substring(p + 1) : invokerName;
76 String className = overrideNames ?
77 CLASS_PREFIX + invokerName.substring(0, p) :
78 CLASS_PREFIX + DEFAULT_CLASS;
79 if (MinimalValueTypes_1_0.DUMP_CLASS_FILES) {
80 // When DUMP_CLASS_FILES is true methodName will have a unique id
81 className = className + "_" + methodName;
82 }
83 return MethodHandleBuilder.loadCode(Lookup.IMPL_LOOKUP.in(LambdaForm.class), className, methodName, invokerType.toMethodDescriptorString(),
84 M -> new LambdaFormCodeBuilder(form, M), clazz -> InvokerBytecodeGenerator.resolveInvokerMember(clazz, methodName, invokerType),
85 C -> new LambdaFormBuilder(C, form, invokerType).generateLambdaFormBody());
86 }
87
88 LambdaFormCodeBuilder builder;
89 LambdaForm lambdaForm;
90 MethodType invokerType;
108 BasicType type = lform.names[i].type();
109 index += type.basicTypeSlots();
110 }
111 }
112 return localsMap;
113 }
114
115 void generateLambdaFormBody() {
116 // iterate over the form's names, generating bytecode instructions for each
117 // start iterating at the first name following the arguments
118 Name onStack = null;
119 for (int i = lambdaForm.arity; i < lambdaForm.names.length; i++) {
120 Name name = lambdaForm.names[i];
121
122 if (onStack != null && onStack.type != V_TYPE) {
123 // non-void: actually assign
124 builder.store(fromBasicType(onStack.type), localsMap[onStack.index()]);
125 }
126 onStack = name; // unless otherwise modified below
127 MemberName member = name.function.member();
128 MethodHandleImpl.Intrinsic intr = name.function.intrinsicName();
129 switch (intr) {
130 case SELECT_ALTERNATIVE: {
131 assert lambdaForm.isSelectAlternative(i);
132 if (PROFILE_GWT) {
133 assert(name.arguments[0] instanceof Name &&
134 ((Name)name.arguments[0]).refersTo(MethodHandleImpl.class, "profileBoolean"));
135 builder.method().withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, InvokerBytecodeGenerator.INJECTEDPROFILE_SIG);
136 }
137 onStack = emitSelectAlternative(name, lambdaForm.names[i+1]);
138 i++; // skip MH.invokeBasic of the selectAlternative result
139 continue;
140
141 }
142 // TODO: case GUARD_WITH_CATCH:
143 // TODO: case TRY_FINALLY:
144
145 case LOOP: {
146 assert lambdaForm.isLoop(i);
147 onStack = emitLoop(i);
148 i += 2; // jump to the end of the LOOP idiom
149 continue;
150 }
151 case IDENTITY: {
152 assert (name.arguments.length == 1);
153 emitPushArguments(name, 0);
154 continue;
155 }
156 case ZERO: {
157 assert (name.arguments.length == 0);
158 assert (name.type != BasicType.Q_TYPE);
159 builder.ldc(name.type.basicTypeWrapper().zero());
160 continue;
161 }
162 case NONE: {
163 // no intrinsic associated
164 break;
165 }
166 // TODO: case NEW_ARRAY:
167 // TODO: case ARRAY_LOAD:
168 // TODO: case ARRAY_STORE:
169 // TODO: case ARRAY_LENGTH:
170 default: {
171 throw newInternalError("Unknown intrinsic: "+intr);
172 }
173 }
174 if (InvokerBytecodeGenerator.isStaticallyInvocable(member)) {
175 emitStaticInvoke(member, name);
176 } else {
177 emitInvoke(name);
178 }
179 }
180 emitReturn(onStack);
181 }
182
183 /**
184 * Emits a return statement from a LF invoker. If required, the result type is cast to the correct return type.
185 */
186 private void emitReturn(Name onStack) {
187 // return statement
188 Class<?> rclass = invokerType.returnType();
189 BasicType rtype = lambdaForm.returnType();
190 assert(rtype == basicType(rclass)); // must agree
191 if (rtype == V_TYPE) {
192 // void
193 builder.return_();
294 // invocation
295 if (member.isMethod()) {
296 mtype = member.getMethodType().toMethodDescriptorString();
297 builder.invoke(invKindFromRefKind(refKind), defc, mname, mtype,
298 member.getDeclaringClass().isInterface());
299 } else {
300 mtype = MethodType.toFieldDescriptorString(member.getFieldType());
301 builder.getfield(fieldKindFromRefKind(refKind), defc, mname, mtype);
302 }
303 // Issue a type assertion for the result, so we can avoid casts later.
304 if (name.type == L_TYPE) {
305 Class<?> rtype = member.getInvocationType().returnType();
306 assert(!rtype.isPrimitive());
307 }
308 }
309
310 /**
311 * Emit an invoke for the given name.
312 */
313 void emitInvoke(Name name) {
314 if (true) {
315 // push receiver
316 MethodHandle target = name.function.resolvedHandle();
317 assert(target != null) : name.exprString();
318 builder.ldc(target);
319 emitReferenceCast(MethodHandle.class, target);
320 }
321
322 // push arguments
323 emitPushArguments(name);
324
325 // invocation
326 MethodType type = name.function.methodType();
327 builder.invokevirtual(MethodHandle.class, "invokeBasic", type.basicType().toMethodDescriptorString(), false);
328 }
329
330 private void emitPushArguments(Name args) {
331 emitPushArguments(args, 0);
332 }
333
358 builder.ldc(arg);
359 emitImplicitConversion(L_TYPE, ptype, arg);
360 }
361 }
362 }
363
364 TypeTag fromBasicType(BasicType type) {
365 switch (type) {
366 case I_TYPE: return TypeTag.I;
367 case J_TYPE: return TypeTag.J;
368 case F_TYPE: return TypeTag.F;
369 case D_TYPE: return TypeTag.D;
370 case L_TYPE: return TypeTag.A;
371 case V_TYPE: return TypeTag.V;
372 case Q_TYPE: return TypeTag.Q;
373 default:
374 throw new InternalError("unknown type: " + type);
375 }
376 }
377
378 TypeTag fromClass(Class<?> cls) {
379 return fromBasicType(BasicType.basicType(cls));
380 }
381
382 InvocationKind invKindFromRefKind(int refKind) {
383 switch (refKind) {
384 case REF_invokeVirtual: return InvocationKind.INVOKEVIRTUAL;
385 case REF_invokeStatic: return InvocationKind.INVOKESTATIC;
386 case REF_invokeSpecial: return InvocationKind.INVOKESPECIAL;
387 case REF_invokeInterface: return InvocationKind.INVOKEINTERFACE;
388 }
389 throw new InternalError("refKind="+refKind);
390 }
391
392 FieldAccessKind fieldKindFromRefKind(int refKind) {
393 switch (refKind) {
394 case REF_getField:
395 case REF_putField: return FieldAccessKind.INSTANCE;
396 case REF_getStatic:
397 case REF_putStatic: return FieldAccessKind.STATIC;
398 }
399 throw new InternalError("refKind="+refKind);
400 }
401
402 static class LambdaFormCodeBuilder extends MethodHandleCodeBuilder {
403
404 private MethodBuilder<Class<?>, String, byte[]> methodBuilder;
405
406 public LambdaFormCodeBuilder(LambdaForm form, MethodBuilder<Class<?>, String, byte[]> methodBuilder) {
407 super(methodBuilder);
408 if (form.forceInline) {
409 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljdk/internal/vm/annotation/ForceInline;");
410 }
411 methodBuilder.withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Hidden;")
412 .withAnnotation(AnnotationsBuilder.Kind.RUNTIME_VISIBLE, "Ljava/lang/invoke/LambdaForm$Compiled;");
413 }
414
415 Class<?> typeOfLocal(int index) {
416 return typeHelper.symbol(state.locals.get(index));
417 }
418
419 MethodBuilder<Class<?>, String, byte[]> method() {
420 return methodBuilder;
421 }
422 }
423
424 private Name emitLoop(int pos) {
425 Name args = lambdaForm.names[pos];
426 Name invoker = lambdaForm.names[pos+1];
427 Name result = lambdaForm.names[pos+2];
428
429 // extract clause and loop-local state types
430 // find the type info in the loop invocation
431 BasicType[] loopClauseTypes = (BasicType[]) invoker.arguments[0];
432 Class<?>[] loopLocalStateTypes = Stream.of(loopClauseTypes).
433 filter(bt -> bt != BasicType.V_TYPE).map(BasicType::basicTypeClass).toArray(Class<?>[]::new);
434
435 Class<?>[] localTypes = new Class<?>[loopLocalStateTypes.length + 1];
436 localTypes[0] = MethodHandleImpl.LoopClauses.class;
437 System.arraycopy(loopLocalStateTypes, 0, localTypes, 1, loopLocalStateTypes.length);
438
439 final int clauseDataIndex = extendLocalsMap(localTypes);
440 final int firstLoopStateIndex = clauseDataIndex + 1;
441 maxLocals += loopLocalStateTypes.length + 1;
442
443 Class<?> returnType = result.function.resolvedHandle().type().returnType();
444 MethodType loopType = args.function.resolvedHandle().type()
445 .dropParameterTypes(0,1)
446 .changeReturnType(returnType);
447 MethodType loopHandleType = loopType.insertParameterTypes(0, loopLocalStateTypes);
448 MethodType predType = loopHandleType.changeReturnType(boolean.class);
449 MethodType finiType = loopHandleType;
450
451 final int nClauses = loopClauseTypes.length;
452
453 // indices to invoker arguments to load method handle arrays
454 final int inits = 1;
455 final int steps = 2;
456 final int preds = 3;
457 final int finis = 4;
458
459 // PREINIT:
460 emitPushArgument(MethodHandleImpl.LoopClauses.class, invoker.arguments[1]);
461 builder.getfield(MethodHandleImpl.LoopClauses.class, "clauses", MHARY2);
462 emitAstoreInsn(clauseDataIndex);
463
464 // INIT:
465 for (int c = 0, state = 0; c < nClauses; ++c) {
466 MethodType cInitType = loopType.changeReturnType(loopClauseTypes[c].basicTypeClass());
467 emitLoopHandleInvoke(inits, c, args, false, cInitType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
468 if (cInitType.returnType() != void.class) {
469 builder.store(fromClass(cInitType.returnType()), localsMap[firstLoopStateIndex + state]);
470 ++state;
471 }
472 }
473
474 // LOOP:
475 builder.label("LOOP");
476
477 String val = null;
478 for (int c = 0, s = 0; c < nClauses; ++c) {
479 MethodType stepType = loopHandleType.changeReturnType(loopClauseTypes[c].basicTypeClass());
480 boolean isVoid = (stepType.returnType() == void.class);
481
482 // invoke loop step
483 emitLoopHandleInvoke(steps, c, args, true, stepType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
484 if (!isVoid) {
485 builder.store(fromClass(stepType.returnType()), localsMap[firstLoopStateIndex + s]);
486 ++s;
487 }
488
489 // invoke loop predicate
490 emitLoopHandleInvoke(preds, c, args, true, predType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
491 builder.emitCondJump(Opcode.IFEQ, NE, "NEXT_" + c);
492
493 // invoke fini
494 emitLoopHandleInvoke(finis, c, args, true, finiType, loopLocalStateTypes, clauseDataIndex, firstLoopStateIndex);
495 builder.goto_("DONE");
496
497 // this is the beginning of the next loop clause
498 builder.label("NEXT_" + c);
499 }
500 builder.goto_("LOOP");
501
502
503 builder.label("DONE");
504
505 return result;
506 }
507
508 private void emitLoopHandleInvoke(int handles, int clause, Name args, boolean pushLocalState,
509 MethodType type, Class<?>[] loopLocalStateTypes, int clauseDataSlot,
510 int firstLoopStateSlot) {
511 // load handle for clause
512 builder.load(TypeTag.A, localsMap[clauseDataSlot])
513 .ldc(handles - 1)
514 .aaload()
515 .ldc(clause)
516 .aaload();
517
518 // load loop state (preceding the other arguments)
519 if (pushLocalState) {
520 for (int s = 0; s < loopLocalStateTypes.length; ++s) {
521 builder.load(fromClass(loopLocalStateTypes[s]), localsMap[firstLoopStateSlot + s]);
522 }
523 }
524 // load loop args (skip 0: method handle)
525 emitPushArguments(args, 1);
526 builder.invokevirtual(MethodHandle.class, "invokeBasic", type.toMethodDescriptorString(), false);
527 }
528
529 /**
530 * Emit bytecode for the selectAlternative idiom.
531 *
532 * The pattern looks like (Cf. MethodHandleImpl.makeGuardWithTest):
533 * <blockquote><pre>{@code
534 * Lambda(a0:L,a1:I)=>{
535 * t2:I=foo.test(a1:I);
536 * t3:L=MethodHandleImpl.selectAlternative(t2:I,(MethodHandle(int)int),(MethodHandle(int)int));
537 * t4:I=MethodHandle.invokeBasic(t3:L,a1:I);t4:I}
538 * }</pre></blockquote>
539 */
540 private Name emitSelectAlternative(Name selectAlternativeName, Name invokeBasicName) {
541 assert InvokerBytecodeGenerator.isStaticallyInvocable(invokeBasicName);
542
543 Name receiver = (Name) invokeBasicName.arguments[0];
544
545 int pos = invokeBasicName.index();
546 String L_fallback = "L_fallback_" + pos;
547 String L_done = "L_done_"+ pos;
548
549 // load test result
550 emitPushArgument(selectAlternativeName, 0);
551
552 // if_icmpne L_fallback
553 builder.emitCondJump(Opcode.IFEQ, NE, L_fallback);
554
555 // invoke selectAlternativeName.arguments[1]
556 emitPushArgument(selectAlternativeName, 1); // get 2nd argument of selectAlternative
557 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
558 emitStaticInvoke(invokeBasicName);
559
560 // goto L_done
561 builder.goto_(L_done);
562
563
564
565 // L_fallback:
566 builder.label(L_fallback);
567
568 // invoke selectAlternativeName.arguments[2]
569 emitPushArgument(selectAlternativeName, 2); // get 3rd argument of selectAlternative
570 emitAstoreInsn(receiver.index()); // store the MH in the receiver slot
571 emitStaticInvoke(invokeBasicName);
572
573 // L_done:
574 builder.label(L_done);
575
576 return invokeBasicName; // return what's on stack
577 }
578
579 private void emitAstoreInsn(int index) {
580 builder.store(TypeTag.A, localsMap[index]);
581 }
582
583 void emitStaticInvoke(Name name) {
584 emitStaticInvoke(name.function.member(), name);
585 }
586
587 private int extendLocalsMap(Class<?>[] types) {
588 int firstSlot = localsMap.length - 1;
589 localsMap = Arrays.copyOf(localsMap, localsMap.length + types.length);
590 int index = localsMap[firstSlot - 1] + 1;
591 int lastSlots = 0;
592 for (int i = 0; i < types.length; ++i) {
593 localsMap[firstSlot + i] = index;
594 lastSlots = BasicType.basicType(types[i]).basicTypeSlots();
595 index += lastSlots;
596 }
597 localsMap[localsMap.length - 1] = index - lastSlots;
598 return firstSlot;
599 }
600 }
|