Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions test/jdk/java/lang/reflect/Proxy/BridgeMethodsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/*
* 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 java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.Callable;
import java.util.function.Consumer;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/*
* @test
* @bug 8370839
* @summary Behavior of bridge methods in interfaces
* @run junit BridgeMethodsTest
*/
public class BridgeMethodsTest {

interface StringCallable extends Callable<String> {
@Override
String call(); // throws no exception
}

// Does not duplicate with Object::clone so it is not proxied
@Test
void testExceptionTypes() throws Throwable {
class MyException extends Exception {}
var instance = Proxy.newProxyInstance(StringCallable.class.getClassLoader(),
new Class[] { StringCallable.class }, (_, _, _) -> { throw new MyException(); });
// The exception can't be thrown through StringCallable.call which has no throws
var undeclared = assertThrows(UndeclaredThrowableException.class, () -> ((StringCallable) instance).call());
assertInstanceOf(MyException.class, undeclared.getCause());
// But it can be thrown through Callable.call which permits Exception
assertThrows(MyException.class, () -> ((Callable<?>) instance).call());
}

interface SpecificConsumer extends Consumer<String> {
@Override
void accept(String s);
}

@Test
@SuppressWarnings("unchecked")
void testMethodObjects() throws Throwable {
List<Method> methods = new ArrayList<>();
var instance = Proxy.newProxyInstance(SpecificConsumer.class.getClassLoader(),
new Class[] { SpecificConsumer.class }, (_, m, _) -> methods.add(m));
((Consumer<Object>) instance).accept(null);
((SpecificConsumer) instance).accept(null);
assertEquals(2, methods.size());
// invocation handler gets different method due to covariant parameter types
assertNotEquals(methods.getFirst(), methods.getLast());
}
}
75 changes: 75 additions & 0 deletions test/jdk/java/lang/reflect/Proxy/NonPublicSignaturesTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
/*
* 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 java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicReference;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/*
* @test
* @bug 8370839
* @summary Behavior of methods whose signature has package-private
* class or interfaces but the proxy interface is public
* @run junit NonPublicSignaturesTest
*/
public class NonPublicSignaturesTest {
enum Internal { INSTANCE }

public interface InternalParameter {
void call(Internal parameter);
}

@Test
void testNonPublicParameter() throws Throwable {
// Creation should be successful
InternalParameter instance = (InternalParameter) Proxy.newProxyInstance(
InternalParameter.class.getClassLoader(),
new Class[] { InternalParameter.class },
(_, _, _) -> null);
instance.call(null);
instance.call(Internal.INSTANCE);
}

public interface InternalReturn {
Internal call();
}

@Test
void testNonPublicReturn() throws Throwable {
AtomicReference<Internal> returnValue = new AtomicReference<>();
// Creation should be successful
InternalReturn instance = (InternalReturn) Proxy.newProxyInstance(
InternalReturn.class.getClassLoader(),
new Class[] { InternalReturn.class },
(_, _, _) -> returnValue.get());
// checkcast does not perform access check for null
returnValue.set(null);
instance.call();
// checkcast fails - proxy class cannot access the return type
returnValue.set(Internal.INSTANCE);
assertThrows(IllegalAccessError.class, instance::call);
}
}
151 changes: 151 additions & 0 deletions test/jdk/java/lang/reflect/Proxy/ProtectedObjectMethodsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
/*
* 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 java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Proxy;
import java.util.concurrent.atomic.AtomicInteger;

import org.junit.jupiter.api.Test;

import static org.junit.jupiter.api.Assertions.*;

/*
* @test
* @bug 8370839
* @summary Behavior of protected methods in java.lang.Object
* @modules java.base/java.lang:+open
* @run junit ProtectedObjectMethodsTest
*/
public class ProtectedObjectMethodsTest {

static final MethodHandle OBJECT_CLONE;
static final MethodHandle OBJECT_FINALIZE;

static {
try {
var lookup = MethodHandles.privateLookupIn(Object.class, MethodHandles.lookup());
OBJECT_CLONE = lookup.findVirtual(Object.class, "clone", MethodType.methodType(Object.class));
OBJECT_FINALIZE = lookup.findVirtual(Object.class, "finalize", MethodType.methodType(void.class));
} catch (ReflectiveOperationException ex) {
throw new ExceptionInInitializerError(ex);
}
}

interface FakeClone {
FakeClone clone();
}

interface TrueClone {
Object clone();
}

interface PrimitiveClone {
int clone();
}

// Does not duplicate with Object::clone so it is not proxied
@Test
void testDistinctClone() throws Throwable {
{
var fake = (FakeClone) Proxy.newProxyInstance(FakeClone.class.getClassLoader(), new Class[] { FakeClone.class }, (p, _, _) -> p);
assertSame(fake, fake.clone());
assertThrows(CloneNotSupportedException.class, () -> {
var _ = (Object) OBJECT_CLONE.invoke((Object) fake);
});
}

{
var fake = (FakeClone) Proxy.newProxyInstance(FakeClone.class.getClassLoader(), new Class[] { FakeClone.class, Cloneable.class }, (p, _, _) -> p);
assertSame(fake, fake.clone());
var fakeClone = (Object) OBJECT_CLONE.invoke((Object) fake);
assertNotSame(fake, fakeClone);
assertSame(fake.getClass(), fakeClone.getClass());
assertSame(fakeClone, ((FakeClone) fakeClone).clone());
}

{
var instance = (PrimitiveClone) Proxy.newProxyInstance(PrimitiveClone.class.getClassLoader(), new Class[] { PrimitiveClone.class }, (_, _, _) -> 42);
assertEquals(42, instance.clone());
assertThrows(CloneNotSupportedException.class, () -> {
var _ = (Object) OBJECT_CLONE.invoke((Object) instance);
});
}

{
var instance = (PrimitiveClone) Proxy.newProxyInstance(PrimitiveClone.class.getClassLoader(), new Class[] { PrimitiveClone.class, Cloneable.class }, (_, _, _) -> 76);
assertEquals(76, instance.clone());
var clone = (Object) OBJECT_CLONE.invoke((Object) instance);
assertNotSame(instance, clone);
assertSame(instance.getClass(), clone.getClass());
assertEquals(76, ((PrimitiveClone) clone).clone());
}
}

// Duplicates with Object::clone so it is proxied
@Test
void testDuplicateClone() throws Throwable {
{
// TrueClone::clone accidentally overrides Object::clone
var instance = (TrueClone) Proxy.newProxyInstance(TrueClone.class.getClassLoader(), new Class[] { TrueClone.class }, (p, _, _) -> p);
assertSame(instance, instance.clone());
assertSame(instance, (Object) OBJECT_CLONE.invoke((Object) instance));
}

{
// Use the TrueClone to bridge Object::clone and FakeClone::clone
var instance = Proxy.newProxyInstance(TrueClone.class.getClassLoader(), new Class[] { TrueClone.class, FakeClone.class }, (p, _, _) -> p);
assertSame(instance, ((FakeClone) instance).clone());
assertSame(instance, ((TrueClone) instance).clone());
assertSame(instance, (Object) OBJECT_CLONE.invoke((Object) instance));
}
}

interface FalseFinalize {
int finalize();
}

interface TrueFinalize {
void finalize();
}

@Test
void testDistinctFinalize() throws Throwable {
AtomicInteger invokeCount = new AtomicInteger();
var instance = Proxy.newProxyInstance(FalseFinalize.class.getClassLoader(), new Class[] { FalseFinalize.class }, (_, _, _) -> invokeCount.incrementAndGet());
OBJECT_FINALIZE.invoke(instance);
assertEquals(0, invokeCount.get());
assertEquals(1, ((FalseFinalize) instance).finalize());
}

@Test
void testDuplicateFinalize() throws Throwable {
AtomicInteger invokeCount = new AtomicInteger();
var instance = Proxy.newProxyInstance(TrueFinalize.class.getClassLoader(), new Class[] { TrueFinalize.class }, (_, _, _) -> invokeCount.incrementAndGet());
OBJECT_FINALIZE.invoke(instance);
assertEquals(1, invokeCount.get());
((TrueFinalize) instance).finalize();
assertEquals(2, invokeCount.get());
}
}