141 lines
5.8 KiB
Java
141 lines
5.8 KiB
Java
/*
|
|
* Copyright (c) 2025, 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.
|
|
*/
|
|
|
|
import org.objectweb.asm.ClassWriter;
|
|
import org.objectweb.asm.MethodVisitor;
|
|
import org.objectweb.asm.Opcodes;
|
|
|
|
import static org.objectweb.asm.ClassWriter.COMPUTE_FRAMES;
|
|
import static org.objectweb.asm.ClassWriter.COMPUTE_MAXS;
|
|
import static org.objectweb.asm.Opcodes.*;
|
|
|
|
/*
|
|
* @test id=defaults
|
|
* @bug 8352075
|
|
* @library /test/lib
|
|
* @library /testlibrary/asm
|
|
* @run main/othervm LocalFieldLookupTest
|
|
*/
|
|
/*
|
|
* @test id=custom-threshold
|
|
* @bug 8352075
|
|
* @library /test/lib
|
|
* @library /testlibrary/asm
|
|
* @requires vm.debug == true
|
|
* @run main/othervm LocalFieldLookupTest
|
|
* @run main/othervm -XX:BinarySearchThreshold=0 LocalFieldLookupTest
|
|
* @run main/othervm -XX:BinarySearchThreshold=1 LocalFieldLookupTest
|
|
* @run main/othervm -XX:BinarySearchThreshold=15 LocalFieldLookupTest
|
|
* @run main/othervm -XX:BinarySearchThreshold=100000 LocalFieldLookupTest
|
|
*/
|
|
public class LocalFieldLookupTest {
|
|
private static final String TEST_CLASS_NAME = "Test";
|
|
private static final int MAX_FIELDS_IN_METHOD = 10000;
|
|
|
|
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException {
|
|
// Test small classes, covering the tested thresholds
|
|
for (int i = 0; i <= 33; ++i) {
|
|
makeClass(i).newInstance();
|
|
}
|
|
// Test classes around 256 fields (index encoding 1/2 bytes) to check off-by-one errors
|
|
for (int i = 254; i <= 259; ++i) {
|
|
makeClass(255).newInstance();
|
|
}
|
|
// We would like to test #fields that create have the stream about 65536 bytes long;
|
|
// this value is not exposed, though, so these are rather experimentally found values,
|
|
// hence fragile. Moreover, since the stream length is incremented by about 8 bytes
|
|
// for each field we cannot test for off-by-one errors reliably.
|
|
for (int i = 8433; i <= 8437; ++i) {
|
|
makeClass(i).newInstance();
|
|
}
|
|
// The largest class we can create - this one has 65533 entries in the constant pool
|
|
makeClass(26205).newInstance();
|
|
}
|
|
|
|
public static Class<?> makeClass(int fields) throws ClassNotFoundException {
|
|
ClassWriter writer = new ClassWriter(COMPUTE_MAXS | COMPUTE_FRAMES);
|
|
writer.visit(49, Opcodes.ACC_PUBLIC + Opcodes.ACC_SUPER, TEST_CLASS_NAME,null, "java/lang/Object", null);
|
|
|
|
for (int i = 0; i < fields; i += 2) {
|
|
writer.visitField(ACC_PUBLIC, "f" + i, "I", null, null);
|
|
// Let's use duplicate names to confirm search takes signatures into account
|
|
if (i + 1 < fields) {
|
|
writer.visitField(ACC_PUBLIC, "f" + i, "J", null, null);
|
|
}
|
|
}
|
|
// We initialize fields in multiple methods to avoid running into bytecode limit per method
|
|
MethodVisitor fi = null;
|
|
for (int i = 0; i < fields; i+= 2) {
|
|
if (fi == null) {
|
|
fi = writer.visitMethod(ACC_PRIVATE, "init" + i, "()V", null, null);
|
|
fi.visitCode();
|
|
}
|
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
|
fi.visitInsn(Opcodes.ICONST_2);
|
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "I");
|
|
if (i + 1 < fields) {
|
|
fi.visitVarInsn(Opcodes.ALOAD, 0);
|
|
fi.visitInsn(Opcodes.LCONST_1);
|
|
fi.visitFieldInsn(PUTFIELD, TEST_CLASS_NAME, "f" + i, "J");
|
|
}
|
|
if (i % MAX_FIELDS_IN_METHOD == MAX_FIELDS_IN_METHOD - 2) {
|
|
fi.visitInsn(Opcodes.RETURN);
|
|
fi.visitMaxs(0, 0);
|
|
fi.visitEnd();
|
|
fi = null;
|
|
}
|
|
}
|
|
if (fi != null) {
|
|
fi.visitInsn(Opcodes.RETURN);
|
|
fi.visitMaxs(0, 0);
|
|
fi.visitEnd();
|
|
}
|
|
{
|
|
MethodVisitor mv = writer.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
|
|
mv.visitCode();
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, "java/lang/Object", "<init>", "()V", false);
|
|
for (int i = 0; i < fields; i += MAX_FIELDS_IN_METHOD) {
|
|
mv.visitVarInsn(ALOAD, 0);
|
|
mv.visitMethodInsn(INVOKESPECIAL, TEST_CLASS_NAME, "init" + i, "()V", false);
|
|
}
|
|
mv.visitInsn(RETURN);
|
|
mv.visitMaxs(0, 0);
|
|
mv.visitEnd();
|
|
}
|
|
writer.visitEnd();
|
|
|
|
byte[] bytecode = writer.toByteArray();
|
|
ClassLoader cl = new ClassLoader() {
|
|
@Override
|
|
protected Class<?> findClass(String name) throws ClassNotFoundException {
|
|
if (!TEST_CLASS_NAME.equals(name)) {
|
|
throw new ClassNotFoundException();
|
|
}
|
|
return defineClass(TEST_CLASS_NAME, bytecode, 0, bytecode.length);
|
|
}
|
|
};
|
|
return cl.loadClass(TEST_CLASS_NAME);
|
|
}
|
|
}
|