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