1 /* 2 * Copyright (c) 2024, 2025, 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 import jdk.incubator.code.dialect.core.CoreOp; 25 import jdk.incubator.code.interpreter.Interpreter; 26 import org.junit.jupiter.api.Assertions; 27 import org.junit.jupiter.api.Test; 28 29 import java.lang.classfile.ClassFile; 30 import java.lang.classfile.Label; 31 import java.lang.constant.*; 32 import java.lang.invoke.MethodHandles; 33 import java.lang.invoke.StringConcatFactory; 34 35 /* 36 * @test 37 * @modules jdk.incubator.code/jdk.incubator.code.internal 38 * @enablePreview 39 * @run junit TestLiftCustomBytecode 40 */ 41 42 public class TestLiftCustomBytecode { 43 44 @Test 45 public void testBackJumps() throws Throwable { 46 CoreOp.FuncOp f = getFuncOp(ClassFile.of().build(ClassDesc.of("BackJumps"), clb -> 47 clb.withMethodBody("backJumps", MethodTypeDesc.of(ConstantDescs.CD_int, ConstantDescs.CD_int), ClassFile.ACC_STATIC, cob -> { 48 Label l1 = cob.newLabel(); 49 Label l2 = cob.newLabel(); 50 Label l3 = cob.newLabel(); 51 Label l4 = cob.newLabel(); 52 // Code wrapped in back jumps requires multiple passes and block skipping 53 cob.goto_(l1) 54 .labelBinding(l2) 55 .goto_(l3) 56 .labelBinding(l4) 57 .iload(0) 58 .ireturn() 59 .labelBinding(l1) 60 .goto_(l2) 61 .labelBinding(l3) 62 .goto_(l4); 63 })), "backJumps"); 64 65 Assertions.assertEquals(42, (int) Interpreter.invoke(MethodHandles.lookup(), f, 42)); 66 } 67 68 @Test 69 public void testDeepStackJump() throws Throwable { 70 CoreOp.FuncOp f = getFuncOp(ClassFile.of().build(ClassDesc.of("DeepStackJump"), clb -> 71 clb.withMethodBody("deepStackJump", MethodTypeDesc.of(ConstantDescs.CD_long), ClassFile.ACC_STATIC, cob -> { 72 Label l = cob.newLabel(); 73 cob.lconst_1().iconst_1().iconst_2() 74 .goto_(l) 75 .labelBinding(l) 76 .iadd().i2l().ladd() 77 .lreturn(); 78 })), "deepStackJump"); 79 80 Assertions.assertEquals(4, (long) Interpreter.invoke(MethodHandles.lookup(), f)); 81 } 82 83 public record TestRecord(int i, String s) { 84 } 85 86 @Test 87 public void testObjectMethodsIndy() throws Throwable { 88 byte[] testRecord = TestRecord.class.getResourceAsStream("TestLiftCustomBytecode$TestRecord.class").readAllBytes(); 89 CoreOp.FuncOp toString = getFuncOp(testRecord, "toString"); 90 CoreOp.FuncOp hashCode = getFuncOp(testRecord, "hashCode"); 91 CoreOp.FuncOp equals = getFuncOp(testRecord, "equals"); 92 93 TestRecord tr1 = new TestRecord(1, "hi"), tr2 = new TestRecord(2, "bye"), tr3 = new TestRecord(1, "hi"); 94 MethodHandles.Lookup lookup = MethodHandles.lookup(); 95 Assertions.assertEquals(tr1.toString(), (String)Interpreter.invoke(lookup, toString, tr1)); 96 Assertions.assertEquals(tr2.toString(), (String)Interpreter.invoke(lookup, toString, tr2)); 97 Assertions.assertEquals(tr1.hashCode(), (int)Interpreter.invoke(lookup, hashCode, tr1)); 98 Assertions.assertEquals(tr2.hashCode(), (int)Interpreter.invoke(lookup, hashCode, tr2)); 99 Assertions.assertTrue((boolean)Interpreter.invoke(lookup, equals, tr1, tr1)); 100 Assertions.assertFalse((boolean)Interpreter.invoke(lookup, equals, tr1, tr2)); 101 Assertions.assertTrue((boolean)Interpreter.invoke(lookup, equals, tr1, tr3)); 102 Assertions.assertFalse((boolean)Interpreter.invoke(lookup, equals, tr1, "hello")); 103 } 104 105 @Test 106 public void testConstantBootstrapsCondy() throws Throwable { 107 byte[] testCondy = ClassFile.of().build(ClassDesc.of("TestCondy"), clb -> 108 clb.withMethodBody("condyMethod", MethodTypeDesc.of(ConstantDescs.CD_Class), ClassFile.ACC_STATIC, cob -> 109 cob.ldc(DynamicConstantDesc.ofNamed( 110 ConstantDescs.ofConstantBootstrap(ConstantDescs.CD_ConstantBootstraps, "primitiveClass", ConstantDescs.CD_Class), 111 int.class.descriptorString(), 112 ConstantDescs.CD_Class)) 113 .areturn())); 114 115 CoreOp.FuncOp primitiveInteger = getFuncOp(testCondy, "condyMethod"); 116 117 MethodHandles.Lookup lookup = MethodHandles.lookup(); 118 Assertions.assertEquals(int.class, (Class)Interpreter.invoke(lookup, primitiveInteger)); 119 } 120 121 @Test 122 public void testStringMakeConcat() throws Throwable { 123 byte[] testStringMakeConcat = ClassFile.of().build(ClassDesc.of("TestStringMakeConcat"), clb -> 124 clb.withMethodBody("concatMethod", MethodTypeDesc.of(ConstantDescs.CD_String), ClassFile.ACC_STATIC, cob -> 125 cob.ldc("A").ldc("B").ldc("C") 126 .invokedynamic(DynamicCallSiteDesc.of( 127 ConstantDescs.ofCallsiteBootstrap(StringConcatFactory.class.describeConstable().get(), "makeConcat", ConstantDescs.CD_CallSite), 128 MethodTypeDesc.of(ConstantDescs.CD_String, ConstantDescs.CD_String, ConstantDescs.CD_String, ConstantDescs.CD_String))) 129 .areturn())); 130 131 CoreOp.FuncOp concatMethod = getFuncOp(testStringMakeConcat, "concatMethod"); 132 133 MethodHandles.Lookup lookup = MethodHandles.lookup(); 134 Assertions.assertEquals("ABC", (String)Interpreter.invoke(lookup, concatMethod)); 135 } 136 137 static CoreOp.FuncOp getFuncOp(byte[] classdata, String method) { 138 CoreOp.FuncOp flift = BytecodeLift.lift(classdata, method); 139 System.out.println(flift.toText()); 140 return flift; 141 } 142 }