summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
author Almaz Mingaleev <mingaleev@google.com> 2024-08-29 12:57:14 +0000
committer Treehugger Robot <android-test-infra-autosubmit@system.gserviceaccount.com> 2024-09-24 10:15:56 +0000
commit63b8399f3f144769260d93a7a985233e5ac5b910 (patch)
tree2d46e0d91f2df79a80f9ed80cdcb49c08e7de9f6
parent0b8cc45c57bd0d13901e2c1d15f74c8b562fb2d2 (diff)
Add more validation for const-method-handle.
Making it closer to JVMS 4.4.8 and 5.4.3.5. The RI rejects certain constructions at the class verification stage, hence there are separate classes for MethodHandle-s targeting <init> and <clinit> methods. In these cases The RI throws ClassFormatError during class load, but currently we do validations only when actual MethodHandle object is constructed, that's is the reason why test code has ICCE | CFE in catch blocks. That will be addressed in the upcoming CLs. This should not cause any compat issues as even if some of these MethodHandle were constructed successfully, their invocation leads to runtime crashes. Bug: 297147201 Test: ./art/test/testrunner/testrunner.py --host --64 --jvm -b Change-Id: I551b04e3c00ffc8bcdeac4760d9ac4b3bb7b2aff
-rw-r--r--runtime/class_linker.cc69
-rw-r--r--test/2280-const-method-handle-validation/build.py22
-rw-r--r--test/2280-const-method-handle-validation/expected-stderr.txt0
-rw-r--r--test/2280-const-method-handle-validation/expected-stdout.txt0
-rwxr-xr-xtest/2280-const-method-handle-validation/generate-sources29
-rw-r--r--test/2280-const-method-handle-validation/info.txt2
-rwxr-xr-xtest/2280-const-method-handle-validation/javac_post.sh31
-rw-r--r--test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodHandle.java58
-rw-r--r--test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodType.java38
-rw-r--r--test/2280-const-method-handle-validation/src-util/transformer/ConstantTransformer.java229
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeConstructor.java39
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeConstructorForClassInitializer.java43
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeConstructorForInstanceMethod.java41
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeConstructorForStaticMethod.java41
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInstance.java101
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInstanceForClassInitializer.java43
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInstanceForConstructor.java40
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInterface.java87
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInterfaceForClassInitializer.java43
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInterfaceForConstructor.java40
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInterfaceForInstanceMethod.java41
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeInterfaceForStaticMethod.java41
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeSpecial.java104
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeSpecialForClassInitializer.java43
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeSpecialForConstructor.java41
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeStatic.java102
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeStaticForClassInitializer.java43
-rw-r--r--test/2280-const-method-handle-validation/src/InvokeStaticForConstructor.java41
-rw-r--r--test/2280-const-method-handle-validation/src/Main.java45
-rw-r--r--test/2280-const-method-handle-validation/src/PlainGet.java76
-rw-r--r--test/2280-const-method-handle-validation/src/PlainPut.java108
-rw-r--r--test/2280-const-method-handle-validation/src/StaticGet.java76
-rw-r--r--test/2280-const-method-handle-validation/src/StaticPut.java108
33 files changed, 1841 insertions, 24 deletions
diff --git a/runtime/class_linker.cc b/runtime/class_linker.cc
index 2d1bf6f780..c7fbf59420 100644
--- a/runtime/class_linker.cc
+++ b/runtime/class_linker.cc
@@ -66,12 +66,14 @@
#include "class_loader_utils.h"
#include "class_root-inl.h"
#include "class_table-inl.h"
+#include "common_throws.h"
#include "compiler_callbacks.h"
#include "debug_print.h"
#include "debugger.h"
#include "dex/class_accessor-inl.h"
#include "dex/descriptors_names.h"
#include "dex/dex_file-inl.h"
+#include "dex/dex_file.h"
#include "dex/dex_file_annotations.h"
#include "dex/dex_file_exception_helpers.h"
#include "dex/dex_file_loader.h"
@@ -10338,6 +10340,12 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForField(
ThrowIllegalAccessErrorField(referring_class, target_field);
return nullptr;
}
+ // TODO(b/364876321): ResolveField might return instance field when is_static is true and
+ // vice versa.
+ if (UNLIKELY(is_static != target_field->IsStatic())) {
+ ThrowIncompatibleClassChangeErrorField(target_field, is_static, referrer);
+ return nullptr;
+ }
if (UNLIKELY(is_put && target_field->IsFinal())) {
ThrowIllegalAccessErrorField(referring_class, target_field);
return nullptr;
@@ -10430,19 +10438,21 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForMethod(
case DexFile::MethodHandleType::kInvokeStatic: {
kind = mirror::MethodHandle::Kind::kInvokeStatic;
receiver_count = 0;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kStatic);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kStatic);
break;
}
case DexFile::MethodHandleType::kInvokeInstance: {
kind = mirror::MethodHandle::Kind::kInvokeVirtual;
receiver_count = 1;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kVirtual);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kVirtual);
break;
}
case DexFile::MethodHandleType::kInvokeConstructor: {
@@ -10450,10 +10460,11 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForMethod(
// are special cased later in this method.
kind = mirror::MethodHandle::Kind::kInvokeTransform;
receiver_count = 0;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kDirect);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
break;
}
case DexFile::MethodHandleType::kInvokeDirect: {
@@ -10475,16 +10486,18 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForMethod(
if (target_method->IsPrivate()) {
kind = mirror::MethodHandle::Kind::kInvokeDirect;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kDirect);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kDirect);
} else {
kind = mirror::MethodHandle::Kind::kInvokeSuper;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kSuper);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kSuper);
if (UNLIKELY(target_method == nullptr)) {
break;
}
@@ -10500,10 +10513,11 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForMethod(
case DexFile::MethodHandleType::kInvokeInterface: {
kind = mirror::MethodHandle::Kind::kInvokeInterface;
receiver_count = 1;
- target_method = ResolveMethod<ResolveMode::kNoChecks>(self,
- method_handle.field_or_method_idx_,
- referrer,
- InvokeType::kInterface);
+ target_method =
+ ResolveMethod<ResolveMode::kCheckICCEAndIAE>(self,
+ method_handle.field_or_method_idx_,
+ referrer,
+ InvokeType::kInterface);
break;
}
}
@@ -10513,6 +10527,13 @@ ObjPtr<mirror::MethodHandle> ClassLinker::ResolveMethodHandleForMethod(
return nullptr;
}
+ // According to JVMS 4.4.8 none of invoke* MethodHandle-s can target <clinit> methods.
+ if (UNLIKELY(target_method->IsClassInitializer())) {
+ ThrowClassFormatError(referrer->GetDeclaringClass(),
+ "Method handles can't target class initializer method");
+ return nullptr;
+ }
+
ObjPtr<mirror::Class> target_class = target_method->GetDeclaringClass();
ObjPtr<mirror::Class> referring_class = referrer->GetDeclaringClass();
uint32_t access_flags = target_method->GetAccessFlags();
diff --git a/test/2280-const-method-handle-validation/build.py b/test/2280-const-method-handle-validation/build.py
new file mode 100644
index 0000000000..ae1a042d40
--- /dev/null
+++ b/test/2280-const-method-handle-validation/build.py
@@ -0,0 +1,22 @@
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+
+def build(ctx):
+ ctx.bash("./generate-sources")
+ # Setting javac_source and javac_target for private interface methods.
+ ctx.default_build(api_level="const-method-type",
+ javac_source_arg="17",
+ javac_target_arg="17")
diff --git a/test/2280-const-method-handle-validation/expected-stderr.txt b/test/2280-const-method-handle-validation/expected-stderr.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/2280-const-method-handle-validation/expected-stderr.txt
diff --git a/test/2280-const-method-handle-validation/expected-stdout.txt b/test/2280-const-method-handle-validation/expected-stdout.txt
new file mode 100644
index 0000000000..e69de29bb2
--- /dev/null
+++ b/test/2280-const-method-handle-validation/expected-stdout.txt
diff --git a/test/2280-const-method-handle-validation/generate-sources b/test/2280-const-method-handle-validation/generate-sources
new file mode 100755
index 0000000000..d74ca98a8c
--- /dev/null
+++ b/test/2280-const-method-handle-validation/generate-sources
@@ -0,0 +1,29 @@
+#!/bin/bash
+#
+# Copyright 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# make us exit on a failure
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.6.jar"
+
+# Build the transformer to apply to compiled classes.
+mkdir classes
+${JAVAC:-javac} ${JAVAC_ARGS} -cp "${ASM_JAR}" -d classes $(find src-util -name '*.java')
+${SOONG_ZIP} --jar -o transformer.jar -C classes -D classes
+rm -rf classes
+
+# Add annotation src files to our compiler inputs.
+cp -r src-util/annotations src/
diff --git a/test/2280-const-method-handle-validation/info.txt b/test/2280-const-method-handle-validation/info.txt
new file mode 100644
index 0000000000..672ec07989
--- /dev/null
+++ b/test/2280-const-method-handle-validation/info.txt
@@ -0,0 +1,2 @@
+Ensures that MethodHandle objects correspoding to invalid const-method-type instructions are not
+created.
diff --git a/test/2280-const-method-handle-validation/javac_post.sh b/test/2280-const-method-handle-validation/javac_post.sh
new file mode 100755
index 0000000000..d0699d3cd7
--- /dev/null
+++ b/test/2280-const-method-handle-validation/javac_post.sh
@@ -0,0 +1,31 @@
+#!/bin/bash
+#
+# Copyright (C) 2024 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+set -e
+
+export ASM_JAR="${ANDROID_BUILD_TOP}/prebuilts/misc/common/asm/asm-9.6.jar"
+
+# Move original classes to intermediate location.
+mv $1 $1-intermediate-classes
+mkdir $1
+
+# Transform intermediate classes.
+transformer_args="-cp ${ASM_JAR}:$PWD/transformer.jar transformer.ConstantTransformer"
+for class in $1-intermediate-classes/*.class ; do
+ transformed_class=$1/$(basename ${class})
+ ${JAVA:-java} ${transformer_args} ${class} ${transformed_class}
+done
+
diff --git a/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodHandle.java b/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodHandle.java
new file mode 100644
index 0000000000..ffc88b65fb
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodHandle.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be set on method to specify that if this method
+ * is statically invoked then the invocation is replaced by a
+ * load-constant bytecode with the MethodHandle constant described by
+ * the annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ConstantMethodHandle {
+ /* Method handle kinds */
+ public static final int STATIC_PUT = 0;
+ public static final int STATIC_GET = 1;
+ public static final int INSTANCE_PUT = 2;
+ public static final int INSTANCE_GET = 3;
+ public static final int INVOKE_STATIC = 4;
+ public static final int INVOKE_VIRTUAL = 5;
+ public static final int INVOKE_SPECIAL = 6;
+ public static final int NEW_INVOKE_SPECIAL = 7;
+ public static final int INVOKE_INTERFACE = 8;
+
+ /** Kind of method handle. */
+ int kind();
+
+ /** Class name owning the field or method. */
+ String owner();
+
+ /** The field or method name addressed by the MethodHandle. */
+ String fieldOrMethodName();
+
+ /** Descriptor for the field (type) or method (method-type) */
+ String descriptor();
+
+ /** Whether the owner is an interface. */
+ boolean ownerIsInterface() default false;
+}
diff --git a/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodType.java b/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodType.java
new file mode 100644
index 0000000000..55b1536e6c
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src-util/annotations/ConstantMethodType.java
@@ -0,0 +1,38 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package annotations;
+
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * This annotation can be set on method to specify that if this method
+ * is statically invoked then the invocation is replaced by a
+ * load-constant bytecode with the MethodType constant described by
+ * the annotation.
+ */
+@Retention(RetentionPolicy.RUNTIME)
+@Target(ElementType.METHOD)
+public @interface ConstantMethodType {
+ /** Return type of method() or field getter() */
+ Class<?> returnType() default void.class;
+
+ /** Types of parameters for method or field setter() */
+ Class<?>[] parameterTypes() default {};
+}
diff --git a/test/2280-const-method-handle-validation/src-util/transformer/ConstantTransformer.java b/test/2280-const-method-handle-validation/src-util/transformer/ConstantTransformer.java
new file mode 100644
index 0000000000..45e7b9ccdb
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src-util/transformer/ConstantTransformer.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package transformer;
+
+import annotations.ConstantMethodHandle;
+import annotations.ConstantMethodType;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodType;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.net.URL;
+import java.net.URLClassLoader;
+import java.nio.file.Files;
+import java.nio.file.Path;
+import java.nio.file.Paths;
+import java.util.HashMap;
+import java.util.Map;
+import org.objectweb.asm.ClassReader;
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.ClassWriter;
+import org.objectweb.asm.Handle;
+import org.objectweb.asm.MethodVisitor;
+import org.objectweb.asm.Opcodes;
+import org.objectweb.asm.Type;
+
+/**
+ * Class for transforming invoke static bytecodes into constant method handle loads and constant
+ * method type loads.
+ *
+ * <p>When a parameterless private static method returning a MethodHandle is defined and annotated
+ * with {@code ConstantMethodHandle}, this transformer will replace static invocations of the method
+ * with a load constant bytecode with a method handle in the constant pool.
+ *
+ * <p>Suppose a method is annotated as: <code>
+ * @ConstantMethodHandle(
+ * kind = ConstantMethodHandle.STATIC_GET,
+ * owner = "java/lang/Math",
+ * fieldOrMethodName = "E",
+ * descriptor = "D"
+ * )
+ * private static MethodHandle getMathE() {
+ * unreachable();
+ * return null;
+ * }
+ * </code> Then invocations of {@code getMathE} will be replaced by a load from the constant pool
+ * with the constant method handle described in the {@code ConstantMethodHandle} annotation.
+ *
+ * <p>Similarly, a parameterless private static method returning a {@code MethodType} and annotated
+ * with {@code ConstantMethodType}, will have invocations replaced by a load constant bytecode with
+ * a method type in the constant pool.
+ */
+class ConstantTransformer {
+ static class ConstantBuilder extends ClassVisitor {
+ private final Map<String, ConstantMethodHandle> constantMethodHandles;
+ private final Map<String, ConstantMethodType> constantMethodTypes;
+
+ ConstantBuilder(
+ int api,
+ ClassVisitor cv,
+ Map<String, ConstantMethodHandle> constantMethodHandles,
+ Map<String, ConstantMethodType> constantMethodTypes) {
+ super(api, cv);
+ this.constantMethodHandles = constantMethodHandles;
+ this.constantMethodTypes = constantMethodTypes;
+ }
+
+ @Override
+ public MethodVisitor visitMethod(
+ int access, String name, String desc, String signature, String[] exceptions) {
+ MethodVisitor mv = cv.visitMethod(access, name, desc, signature, exceptions);
+ return new MethodVisitor(this.api, mv) {
+ @Override
+ public void visitMethodInsn(
+ int opcode, String owner, String name, String desc, boolean itf) {
+ if (opcode == org.objectweb.asm.Opcodes.INVOKESTATIC) {
+ ConstantMethodHandle constantMethodHandle = constantMethodHandles.get(name);
+ if (constantMethodHandle != null) {
+ insertConstantMethodHandle(constantMethodHandle);
+ return;
+ }
+ ConstantMethodType constantMethodType = constantMethodTypes.get(name);
+ if (constantMethodType != null) {
+ insertConstantMethodType(constantMethodType);
+ return;
+ }
+ }
+ mv.visitMethodInsn(opcode, owner, name, desc, itf);
+ }
+
+ private Type buildMethodType(Class<?> returnType, Class<?>[] parameterTypes) {
+ Type rType = Type.getType(returnType);
+ Type[] pTypes = new Type[parameterTypes.length];
+ for (int i = 0; i < pTypes.length; ++i) {
+ pTypes[i] = Type.getType(parameterTypes[i]);
+ }
+ return Type.getMethodType(rType, pTypes);
+ }
+
+ private int getHandleTag(int kind) {
+ switch (kind) {
+ case ConstantMethodHandle.STATIC_PUT:
+ return Opcodes.H_PUTSTATIC;
+ case ConstantMethodHandle.STATIC_GET:
+ return Opcodes.H_GETSTATIC;
+ case ConstantMethodHandle.INSTANCE_PUT:
+ return Opcodes.H_PUTFIELD;
+ case ConstantMethodHandle.INSTANCE_GET:
+ return Opcodes.H_GETFIELD;
+ case ConstantMethodHandle.INVOKE_STATIC:
+ return Opcodes.H_INVOKESTATIC;
+ case ConstantMethodHandle.INVOKE_VIRTUAL:
+ return Opcodes.H_INVOKEVIRTUAL;
+ case ConstantMethodHandle.INVOKE_SPECIAL:
+ return Opcodes.H_INVOKESPECIAL;
+ case ConstantMethodHandle.NEW_INVOKE_SPECIAL:
+ return Opcodes.H_NEWINVOKESPECIAL;
+ case ConstantMethodHandle.INVOKE_INTERFACE:
+ return Opcodes.H_INVOKEINTERFACE;
+ }
+ throw new Error("Unhandled kind " + kind);
+ }
+
+ private void insertConstantMethodHandle(ConstantMethodHandle constantMethodHandle) {
+ Handle handle =
+ new Handle(
+ getHandleTag(constantMethodHandle.kind()),
+ constantMethodHandle.owner(),
+ constantMethodHandle.fieldOrMethodName(),
+ constantMethodHandle.descriptor(),
+ constantMethodHandle.ownerIsInterface());
+ mv.visitLdcInsn(handle);
+ }
+
+ private void insertConstantMethodType(ConstantMethodType constantMethodType) {
+ Type methodType =
+ buildMethodType(
+ constantMethodType.returnType(),
+ constantMethodType.parameterTypes());
+ mv.visitLdcInsn(methodType);
+ }
+ };
+ }
+ }
+
+ private static void throwAnnotationError(
+ Method method, Class<?> annotationClass, String reason) {
+ StringBuilder sb = new StringBuilder();
+ sb.append("Error in annotation ")
+ .append(annotationClass)
+ .append(" on method ")
+ .append(method)
+ .append(": ")
+ .append(reason);
+ throw new Error(sb.toString());
+ }
+
+ private static void checkMethodToBeReplaced(
+ Method method, Class<?> annotationClass, Class<?> returnType) {
+ final int PRIVATE_STATIC = Modifier.STATIC | Modifier.PRIVATE;
+ if ((method.getModifiers() & PRIVATE_STATIC) != PRIVATE_STATIC) {
+ throwAnnotationError(method, annotationClass, " method is not private and static");
+ }
+ if (method.getTypeParameters().length != 0) {
+ throwAnnotationError(method, annotationClass, " method expects parameters");
+ }
+ if (!method.getReturnType().equals(returnType)) {
+ throwAnnotationError(method, annotationClass, " wrong return type");
+ }
+ }
+
+ private static void transform(Path inputClassPath, Path outputClassPath) throws Throwable {
+ Path classLoadPath = inputClassPath.toAbsolutePath().getParent();
+ URLClassLoader classLoader =
+ new URLClassLoader(new URL[] {classLoadPath.toUri().toURL()},
+ ClassLoader.getSystemClassLoader());
+ String inputClassName = inputClassPath.getFileName().toString().replace(".class", "");
+ Class<?> inputClass = classLoader.loadClass(inputClassName);
+
+ final Map<String, ConstantMethodHandle> constantMethodHandles = new HashMap<>();
+ final Map<String, ConstantMethodType> constantMethodTypes = new HashMap<>();
+
+ for (Method m : inputClass.getDeclaredMethods()) {
+ ConstantMethodHandle constantMethodHandle = m.getAnnotation(ConstantMethodHandle.class);
+ if (constantMethodHandle != null) {
+ checkMethodToBeReplaced(m, ConstantMethodHandle.class, MethodHandle.class);
+ constantMethodHandles.put(m.getName(), constantMethodHandle);
+ continue;
+ }
+
+ ConstantMethodType constantMethodType = m.getAnnotation(ConstantMethodType.class);
+ if (constantMethodType != null) {
+ checkMethodToBeReplaced(m, ConstantMethodType.class, MethodType.class);
+ constantMethodTypes.put(m.getName(), constantMethodType);
+ continue;
+ }
+ }
+ ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_FRAMES);
+ try (InputStream is = Files.newInputStream(inputClassPath)) {
+ ClassReader cr = new ClassReader(is);
+ ConstantBuilder cb =
+ new ConstantBuilder(
+ Opcodes.ASM7, cw, constantMethodHandles, constantMethodTypes);
+ cr.accept(cb, 0);
+ }
+ try (OutputStream os = Files.newOutputStream(outputClassPath)) {
+ os.write(cw.toByteArray());
+ }
+ }
+
+ public static void main(String[] args) throws Throwable {
+ transform(Paths.get(args[0]), Paths.get(args[1]));
+ }
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeConstructor.java b/test/2280-const-method-handle-validation/src/InvokeConstructor.java
new file mode 100644
index 0000000000..4ca22b8e0c
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeConstructor.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class InvokeConstructor {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ public static void runTests() {
+ try {
+ InvokeConstructorForInstanceMethod.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeConstructorForClassInitializer.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeConstructorForStaticMethod.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+ }
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeConstructorForClassInitializer.java b/test/2280-const-method-handle-validation/src/InvokeConstructorForClassInitializer.java
new file mode 100644
index 0000000000..d326e484a5
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeConstructorForClassInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeConstructorForClassInitializer {
+
+ private static int STATIC_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.NEW_INVOKE_SPECIAL,
+ owner = "InvokeConstructorForClassInitializer",
+ fieldOrMethodName = "<clinit>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeConstructorForInstanceMethod.java b/test/2280-const-method-handle-validation/src/InvokeConstructorForInstanceMethod.java
new file mode 100644
index 0000000000..4b9536515a
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeConstructorForInstanceMethod.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeConstructorForInstanceMethod {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.NEW_INVOKE_SPECIAL,
+ owner = "java/lang/Object",
+ fieldOrMethodName = "hashCode",
+ descriptor = "()I")
+ private static MethodHandle forInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forInstanceMethod();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeConstructorForStaticMethod.java b/test/2280-const-method-handle-validation/src/InvokeConstructorForStaticMethod.java
new file mode 100644
index 0000000000..d447b7025e
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeConstructorForStaticMethod.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeConstructorForStaticMethod {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.NEW_INVOKE_SPECIAL,
+ owner = "InvokeConstructorForStaticMethod",
+ fieldOrMethodName = "unreachable",
+ descriptor = "()V")
+ private static MethodHandle forStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forStaticMethod();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInstance.java b/test/2280-const-method-handle-validation/src/InvokeInstance.java
new file mode 100644
index 0000000000..0c8ad69470
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInstance.java
@@ -0,0 +1,101 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInstance {
+
+ private int instanceField;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "InvokeInstance",
+ fieldOrMethodName = "unreachable",
+ descriptor = "()V")
+ private static MethodHandle forStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "java/util/List",
+ fieldOrMethodName = "size",
+ descriptor = "()I")
+ private static MethodHandle forInterfaceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "Main",
+ fieldOrMethodName = "instanceMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessibleInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "Main",
+ fieldOrMethodName = "staticMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessibleStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forStaticMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forInterfaceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ InvokeInstanceForConstructor.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeInstanceForClassInitializer.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ inaccessibleInstanceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInstanceForClassInitializer.java b/test/2280-const-method-handle-validation/src/InvokeInstanceForClassInitializer.java
new file mode 100644
index 0000000000..e648ce037e
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInstanceForClassInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInstanceForClassInitializer {
+
+ private static int STATIC_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "InvokeInstanceForClassInitializer",
+ fieldOrMethodName = "<clinit>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInstanceForConstructor.java b/test/2280-const-method-handle-validation/src/InvokeInstanceForConstructor.java
new file mode 100644
index 0000000000..8d662628e5
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInstanceForConstructor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInstanceForConstructor {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_VIRTUAL,
+ owner = "InvokeInstanceForConstructor",
+ fieldOrMethodName = "<init>",
+ descriptor = "()V")
+ private static MethodHandle forConstructor() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forConstructor();
+ }
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInterface.java b/test/2280-const-method-handle-validation/src/InvokeInterface.java
new file mode 100644
index 0000000000..602eef3ce0
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInterface.java
@@ -0,0 +1,87 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInterface {
+
+ private int instanceField;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "InvokeInterface",
+ fieldOrMethodName = "unreachable",
+ descriptor = "()V")
+ private static MethodHandle forStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "java/lang/Object",
+ fieldOrMethodName = "hashCode",
+ descriptor = "()I")
+ private static MethodHandle forInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "Main$I",
+ fieldOrMethodName = "privateMethod",
+ descriptor = "()V",
+ ownerIsInterface = true)
+ private static MethodHandle inaccessiblePrivateInterfaceMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ InvokeInterfaceForStaticMethod.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeInterfaceForInstanceMethod.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeInterfaceForConstructor.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeInterfaceForClassInitializer.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ inaccessiblePrivateInterfaceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInterfaceForClassInitializer.java b/test/2280-const-method-handle-validation/src/InvokeInterfaceForClassInitializer.java
new file mode 100644
index 0000000000..a837a7a066
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInterfaceForClassInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInterfaceForClassInitializer {
+
+ private static int STATIC_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "InvokeInterfaceForClassInitializer",
+ fieldOrMethodName = "<clinit>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInterfaceForConstructor.java b/test/2280-const-method-handle-validation/src/InvokeInterfaceForConstructor.java
new file mode 100644
index 0000000000..0306f2831f
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInterfaceForConstructor.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInterfaceForConstructor {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "InvokeInterfaceForConstructor",
+ fieldOrMethodName = "<init>",
+ descriptor = "()V")
+ private static MethodHandle forConstructor() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forConstructor();
+ }
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInterfaceForInstanceMethod.java b/test/2280-const-method-handle-validation/src/InvokeInterfaceForInstanceMethod.java
new file mode 100644
index 0000000000..0a6ca3b73a
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInterfaceForInstanceMethod.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInterfaceForInstanceMethod {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "java/lang/Object",
+ fieldOrMethodName = "hashCode",
+ descriptor = "()I")
+ private static MethodHandle forInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forInstanceMethod();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeInterfaceForStaticMethod.java b/test/2280-const-method-handle-validation/src/InvokeInterfaceForStaticMethod.java
new file mode 100644
index 0000000000..1a66eb2909
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeInterfaceForStaticMethod.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeInterfaceForStaticMethod {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_INTERFACE,
+ owner = "InvokeInterfaceForStaticMethod",
+ fieldOrMethodName = "unreachable",
+ descriptor = "()V")
+ private static MethodHandle forStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forStaticMethod();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeSpecial.java b/test/2280-const-method-handle-validation/src/InvokeSpecial.java
new file mode 100644
index 0000000000..f861fc77cb
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeSpecial.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeSpecial {
+
+ private int instanceField;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ public void method() {}
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "InvokeSpecial",
+ fieldOrMethodName = "unreachable",
+ descriptor = "()V")
+ private static MethodHandle forStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "java/util/List",
+ fieldOrMethodName = "size",
+ descriptor = "()I")
+ private static MethodHandle forInterfaceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "Main",
+ fieldOrMethodName = "instanceMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessiblePrivateMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "Main",
+ fieldOrMethodName = "staticMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessibleStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forStaticMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ // TODO(b/297147201): runtime crashes here.
+ /*
+ try {
+ forInterfaceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ */
+
+ // TODO(b/297147201): runtime does not throw exception here.
+ /*
+ try {
+ InvokeSpecialForConstructor.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+ */
+
+ try {
+ InvokeSpecialForClassInitializer.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ inaccessibleStaticMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeSpecialForClassInitializer.java b/test/2280-const-method-handle-validation/src/InvokeSpecialForClassInitializer.java
new file mode 100644
index 0000000000..9258b79593
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeSpecialForClassInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeSpecialForClassInitializer {
+
+ private static int STATIC_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "InvokeSpecialForClassInitializer",
+ fieldOrMethodName = "<clinit>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeSpecialForConstructor.java b/test/2280-const-method-handle-validation/src/InvokeSpecialForConstructor.java
new file mode 100644
index 0000000000..c87509bf57
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeSpecialForConstructor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeSpecialForConstructor {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_SPECIAL,
+ owner = "InvokeSpecialForConstructor",
+ fieldOrMethodName = "<init>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeStatic.java b/test/2280-const-method-handle-validation/src/InvokeStatic.java
new file mode 100644
index 0000000000..638630145b
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeStatic.java
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeStatic {
+ private int instanceField;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ public void method() {}
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "InvokeStatic",
+ fieldOrMethodName = "method",
+ descriptor = "()V")
+ private static MethodHandle forInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "java/util/List",
+ fieldOrMethodName = "size",
+ descriptor = "()I")
+ private static MethodHandle forInterfaceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "Main",
+ fieldOrMethodName = "instanceMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessibleInstanceMethod() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "Main",
+ fieldOrMethodName = "staticMethod",
+ descriptor = "()V")
+ private static MethodHandle inaccessibleStaticMethod() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forInstanceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forInterfaceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ InvokeStaticForConstructor.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ InvokeStaticForClassInitializer.runTests();
+ unreachable();
+ } catch (IncompatibleClassChangeError | ClassFormatError expected) {}
+
+ try {
+ inaccessibleInstanceMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticMethod();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeStaticForClassInitializer.java b/test/2280-const-method-handle-validation/src/InvokeStaticForClassInitializer.java
new file mode 100644
index 0000000000..ebce0ee3d2
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeStaticForClassInitializer.java
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeStaticForClassInitializer {
+
+ private static int STATIC_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "InvokeStaticForClassInitializer",
+ fieldOrMethodName = "<clinit>",
+ descriptor = "()V")
+ private static MethodHandle forClassInitializer() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forClassInitializer();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/InvokeStaticForConstructor.java b/test/2280-const-method-handle-validation/src/InvokeStaticForConstructor.java
new file mode 100644
index 0000000000..7ad189815a
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/InvokeStaticForConstructor.java
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class InvokeStaticForConstructor {
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INVOKE_STATIC,
+ owner = "InvokeStaticForConstructor",
+ fieldOrMethodName = "<init>",
+ descriptor = "()V")
+ private static MethodHandle forConstructor() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ forConstructor();
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/Main.java b/test/2280-const-method-handle-validation/src/Main.java
new file mode 100644
index 0000000000..21713ec5c4
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/Main.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+public class Main {
+
+ private interface I {
+ public void method();
+
+ private void privateMethod() {}
+
+ default void defaultMethod() {}
+ }
+
+ private int privateField;
+ private static int PRIVATE_STATIC_FIELD;
+
+ private void instanceMethod() {}
+ private static void staticMethod() {}
+
+ public static void main(String[] args) {
+ PlainGet.runTests();
+ PlainPut.runTests();
+ StaticGet.runTests();
+ StaticPut.runTests();
+
+ InvokeInstance.runTests();
+ InvokeStatic.runTests();
+ InvokeInterface.runTests();
+ InvokeSpecial.runTests();
+ InvokeConstructor.runTests();
+ }
+}
diff --git a/test/2280-const-method-handle-validation/src/PlainGet.java b/test/2280-const-method-handle-validation/src/PlainGet.java
new file mode 100644
index 0000000000..0b880e5744
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/PlainGet.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class PlainGet {
+
+ private static int STATIC_FIELD;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_GET,
+ owner = "PlainGet",
+ fieldOrMethodName = "STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle forStaticField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_GET,
+ owner = "Main",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle inaccessibleInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_GET,
+ owner = "Main",
+ fieldOrMethodName = "PRIVATE_STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle inaccessibleStaticField() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/PlainPut.java b/test/2280-const-method-handle-validation/src/PlainPut.java
new file mode 100644
index 0000000000..4c5d2833c0
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/PlainPut.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class PlainPut {
+
+ private final int finalField = 2;
+ private static int STATIC_FIELD;
+ private static final int STATIC_FINAL_FIELD = 1;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_PUT,
+ owner = "PlainGet",
+ fieldOrMethodName = "STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle forStaticField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_PUT,
+ owner = "PlainGet",
+ fieldOrMethodName = "finalField",
+ descriptor = "I")
+ private static MethodHandle forFinalField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_PUT,
+ owner = "PlainGet",
+ fieldOrMethodName = "STATIC_FINAL_FIELD",
+ descriptor = "I")
+ private static MethodHandle forStaticFinalField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_PUT,
+ owner = "Main",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle inaccessibleInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.INSTANCE_PUT,
+ owner = "Main",
+ fieldOrMethodName = "PRIVATE_STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle inaccessibleStaticField() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forFinalField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forStaticFinalField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/StaticGet.java b/test/2280-const-method-handle-validation/src/StaticGet.java
new file mode 100644
index 0000000000..8f42db91aa
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/StaticGet.java
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class StaticGet {
+
+ private int instanceField;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_GET,
+ owner = "StaticGet",
+ fieldOrMethodName = "instanceField",
+ descriptor = "I")
+ private static MethodHandle forInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_GET,
+ owner = "Main",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle inaccessibleInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_GET,
+ owner = "Main",
+ fieldOrMethodName = "PRIVATE_STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle inaccessibleStaticField() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}
diff --git a/test/2280-const-method-handle-validation/src/StaticPut.java b/test/2280-const-method-handle-validation/src/StaticPut.java
new file mode 100644
index 0000000000..ef57baabd4
--- /dev/null
+++ b/test/2280-const-method-handle-validation/src/StaticPut.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2024 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.invoke.MethodHandle;
+
+import annotations.ConstantMethodHandle;
+
+public class StaticPut {
+
+ private int instanceField;
+ private final int finalInstanceField = 1;
+ private static final int STATIC_FINAL_FIELD = 2;
+
+ private static void unreachable() {
+ throw new AssertionError("unreachable!");
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_PUT,
+ owner = "StaticPut",
+ fieldOrMethodName = "instanceField",
+ descriptor = "I")
+ private static MethodHandle forInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_PUT,
+ owner = "StaticPut",
+ fieldOrMethodName = "finalInstanceField",
+ descriptor = "I")
+ private static MethodHandle forFinalInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_PUT,
+ owner = "StaticPut",
+ fieldOrMethodName = "STATIC_FINAL_FIELD",
+ descriptor = "I")
+ private static MethodHandle forFinalStaticField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_PUT,
+ owner = "Main",
+ fieldOrMethodName = "privateField",
+ descriptor = "I")
+ private static MethodHandle inaccessibleInstanceField() {
+ unreachable();
+ return null;
+ }
+
+ @ConstantMethodHandle(
+ kind = ConstantMethodHandle.STATIC_PUT,
+ owner = "Main",
+ fieldOrMethodName = "PRIVATE_STATIC_FIELD",
+ descriptor = "I")
+ private static MethodHandle inaccessibleStaticField() {
+ unreachable();
+ return null;
+ }
+
+ public static void runTests() {
+ try {
+ forInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forFinalInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ forFinalStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleInstanceField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+
+ try {
+ inaccessibleStaticField();
+ unreachable();
+ } catch (IncompatibleClassChangeError expected) {}
+ }
+
+}