Skip to content
Permalink
Browse files
8270477: [lworld] bytecode testing API does not emit Q type descriptors
Reviewed-by: mchung
  • Loading branch information
David Simms committed Sep 14, 2021
1 parent 0c0c9e1 commit e075eaf3078a95904eaab48d5600be76ddde7816
@@ -0,0 +1,163 @@
/*
* Copyright (c) 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 only, as
* published by the Free Software Foundation.
*
* This code is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* version 2 for more details (a copy is included in the LICENSE file that
* accompanied this code).
*
* You should have received a copy of the GNU General Public License version
* 2 along with this work; if not, write to the Free Software Foundation,
* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
*
* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
* or visit www.oracle.com if you need additional information or have any
* questions.
*/

package runtime.valhalla.inlinetypes;

import java.lang.invoke.*;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import static jdk.test.lib.Asserts.*;

import jdk.experimental.bytecode.*;

import test.java.lang.invoke.lib.InstructionHelper;

/**
* @test TestBytecodeLib
* @summary Check bytecode test library generates the correct code for Valhalla changes to JVMS
* @library /test/lib /test/jdk/lib/testlibrary/bytecode /test/jdk/java/lang/invoke/common
* @build jdk.experimental.bytecode.BasicClassBuilder test.java.lang.invoke.lib.InstructionHelper
* @compile Point.java TestBytecodeLib.java
* @run main/othervm runtime.valhalla.inlinetypes.TestBytecodeLib
*/

public class TestBytecodeLib {

static MethodHandles.Lookup LOOKUP = MethodHandles.lookup();

public static void main(String[] args) throws Throwable {
testAnewarrayDesc();
testCheckcastDesc();
// No support in test library for "ldc(Class<?>)" at all in this incarnation of the API, skip it
testMultianewarrayDesc();
}

// anewarray accepts reference and inline reference type
// checkcast for arrays accepts reference and inline reference array type
static void testAnewarrayDesc() throws Throwable {
Class<?> lClass = Point.ref.class;
Class<?> qClass = Point.val.class;

String methodName = "anewarrayLQClass";
MethodType methodType = MethodType.methodType(void.class);
byte[] codeBytes = InstructionHelper.buildCode(LOOKUP, methodName, methodType,
CODE -> {
CODE
.iconst_3()
.anewarray(lClass)
.checkcast(Point.ref[].class)
.pop()
.iconst_3()
.anewarray(qClass)
.checkcast(Point.val[].class)
.pop()
.return_();
}
);

// Verify correct byte-code
dumpBytes(methodName + ".class", codeBytes);

// Verify it works
InstructionHelper.loadCodeBytes(LOOKUP, methodName, methodType, codeBytes).invokeExact();
}

// checkcast accepts reference and inline reference type
static void testCheckcastDesc() throws Throwable {
Class<?> lClass = Point.ref.class;
Class<?> qClass = Point.val.class;

String methodName = "checkcastLQClass";
MethodType methodType = MethodType.methodType(void.class);
byte[] codeBytes = InstructionHelper.buildCode(LOOKUP, methodName, methodType,
CODE -> {
CODE
.defaultvalue(Point.class)
.checkcast(lClass) // expect no descriptor here
.checkcast(qClass) // expect Q-type descriptor here
.pop()
.return_();
}
);

// Verify correct byte-code
dumpBytes(methodName + ".class", codeBytes);

// Verify it works
InstructionHelper.loadCodeBytes(LOOKUP, methodName, methodType, codeBytes).invokeExact();
}

// multianewarray accepts reference and inline reference type...it naturally does, but...
// checkcast for multidim arrays accepts reference and inline reference array type
static void testMultianewarrayDesc() throws Throwable {
Class<?> lClass = Point.ref[][].class;
Class<?> qClass = Point.val[][].class;

String methodName = "multianewarrayLQClass";
MethodType methodType = MethodType.methodType(void.class);
byte dimCnt = (byte) 2;
byte[] codeBytes = InstructionHelper.buildCode(LOOKUP, methodName, methodType,
CODE -> {
CODE
.iconst_3()
.iconst_4()
.multianewarray(lClass, dimCnt)
.checkcast(lClass)
.pop()
.iconst_3()
.iconst_4()
.multianewarray(qClass, dimCnt)
.checkcast(qClass)
.pop()
.return_();
}
);

// Verify correct byte-code
dumpBytes(methodName + ".class", codeBytes);

// Verify it works
InstructionHelper.loadCodeBytes(LOOKUP, methodName, methodType, codeBytes).invokeExact();
}

/*
Dump the resulting bytes for inspection.
TODO: Would prefer programmtic use of ClassReader for verification, but only
when the JVMS on q-types is less fluid (since it is a lot of work),
so manual inspection for now.
Dump in the dir above "test-support/<test-suite-run>/scratch/<n>" so it doesn't get clean up at end of run,
and use a directory "DUMP_CLASS_FILES" (in keeping with MethodHandles classfile dump)
*/
static void dumpBytes(String name, byte[] bytes) throws java.io.IOException {
Path path = Paths.get("../DUMP_CLASS_FILES");
Files.createDirectories(path);
path = path.resolve(name);
System.out.println("Dump: " + path);
Files.write(path, bytes);
}


}
@@ -1,5 +1,5 @@
/*
* Copyright (c) 2010, 2017, Oracle and/or its affiliates. All rights reserved.
* Copyright (c) 2010, 2021, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -163,9 +163,9 @@ public static String cref(Class<?> c) {

// loadCode(MethodHandles.Lookup, String, MethodType, Consumer<? super MethodHandleCodeBuilder<?>>) et al...

public static MethodHandle loadCode(MethodHandles.Lookup lookup, String name, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
public static MethodHandle loadCode(MethodHandles.Lookup lookup, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
String className = generateClassNameFromLookupClass(lookup);
return loadCode(lookup, className, name, type, builder);
return loadCode(lookup, className, methodName, type, builder);
}

public static MethodHandle loadCode(MethodHandles.Lookup lookup, String className, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
@@ -181,31 +181,51 @@ public static MethodHandle loadCode(MethodHandles.Lookup lookup, String classNam
builder);
}

// Helper method to load code built with "buildCode()"
public static MethodHandle loadCodeBytes(MethodHandles.Lookup lookup, String methodName, MethodType type, byte[] byteCode) {
try {
Class<?> clazz = lookup.defineClass(byteCode);
return lookup.findStatic(clazz, methodName, type);
} catch (Throwable t) {
throw new RuntimeException("Failed to loadCodeBytes \"" + methodName + "\"", t);
}
}


private static <Z, C extends CodeBuilder<Class<?>, String, byte[], ?>> Z loadCode(
MethodHandles.Lookup lookup, String className, String methodName, String type,
Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
Function<Class<?>, Z> resFunc, Consumer<? super C> builder) {

IsolatedMethodBuilder isolatedMethodBuilder = new IsolatedMethodBuilder(className, lookup);
isolatedMethodBuilder
.withSuperclass(Object.class)
.withMajorVersion(62)
.withMinorVersion(0)
.withFlags(Flag.ACC_PUBLIC)
.withMethod(methodName, type, M ->
M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
.withCode(builderFunc, builder));

try {
byte[] byteArray = isolatedMethodBuilder.build();
byte[] byteArray = buildCode(lookup, className, methodName, type, builderFunc, builder);
Class<?> clazz = lookup.defineClass(byteArray);
return resFunc.apply(clazz);
} catch (Throwable e) {
throw new IllegalStateException(e);
}
}

public static byte[] buildCode(MethodHandles.Lookup lookup, String methodName, MethodType type, Consumer<? super MethodHandleCodeBuilder<?>> builder) {
String className = generateClassNameFromLookupClass(lookup);
return buildCode(lookup, className, methodName, type.toMethodDescriptorString(), MethodHandleCodeBuilder::new, builder);
}

public static <C extends CodeBuilder<Class<?>, String, byte[], ?>> byte[] buildCode(
MethodHandles.Lookup lookup, String className, String methodName, String type,
Function<MethodBuilder<Class<?>, String, byte[]>, ? extends C> builderFunc,
Consumer<? super C> builder) {

return new IsolatedMethodBuilder(className, lookup)
.withSuperclass(Object.class)
.withMajorVersion(62)
.withMinorVersion(0)
.withFlags(Flag.ACC_PUBLIC)
.withMethod(methodName, type, M ->
M.withFlags(Flag.ACC_STATIC, Flag.ACC_PUBLIC)
.withCode(builderFunc, builder)).build();

}

private static class IsolatedMethodBuilder extends ClassBuilder<Class<?>, String, IsolatedMethodBuilder> {

private static final Class<?> THIS_CLASS = new Object() { }.getClass();
@@ -221,6 +241,9 @@ private IsolatedMethodBuilder(String clazz, MethodHandles.Lookup lookup) {
}

static String classToInternalName(Class<?> c) {
if (c.isArray()) {
return c.descriptorString();
}
return c.getName().replace('.', '/');
}

@@ -260,11 +283,7 @@ public String returnType(String s) {

@Override
public String type(Class<?> aClass) {
if (aClass.isArray()) {
return classToInternalName(aClass);
} else {
return (aClass.isValueType() ? "Q" : "L") + classToInternalName(aClass) + ";";
}
return aClass.descriptorString();
}

@Override
@@ -173,19 +173,25 @@ public C newarray(TypeTag tag) {

public C anewarray(S array) {
emitOp(Opcode.ANEWARRAY, array);
code.writeChar(poolHelper.putClass(array));
int poolIdx = (typeHelper.isInlineClass(typeHelper.type(array))) ?
poolHelper.putInlineClass(array) : poolHelper.putClass(array);
code.writeChar(poolIdx);
return thisBuilder();
}

public C checkcast(S target) {
emitOp(Opcode.CHECKCAST);
code.writeChar(poolHelper.putClass(target));
int poolIdx = (typeHelper.isInlineClass(typeHelper.type(target))) ?
poolHelper.putInlineClass(target) : poolHelper.putClass(target);
code.writeChar(poolIdx);
return thisBuilder();
}

public C instanceof_(S target) {
emitOp(Opcode.INSTANCEOF);
code.writeChar(poolHelper.putClass(target));
int poolIdx = (typeHelper.isInlineClass(typeHelper.type(target))) ?
poolHelper.putInlineClass(target) : poolHelper.putClass(target);
code.writeChar(poolIdx);
return thisBuilder();
}

0 comments on commit e075eaf

Please sign in to comment.