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