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