mirror of
https://github.com/openjdk/jdk.git
synced 2026-01-28 12:09:14 +00:00
8164714: Constructor.newInstance creates instance of inner class with null outer class
Reviewed-by: vromero
This commit is contained in:
parent
c856b3425a
commit
60544a15d6
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 1999, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 1999, 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
|
||||
@ -105,6 +105,7 @@ public class Lower extends TreeTranslator {
|
||||
private final boolean disableProtectedAccessors; // experimental
|
||||
private final PkgInfo pkginfoOpt;
|
||||
private final boolean optimizeOuterThis;
|
||||
private final boolean nullCheckOuterThis;
|
||||
private final boolean useMatchException;
|
||||
private final HashMap<TypePairs, String> typePairToName;
|
||||
private int variableIndex = 0;
|
||||
@ -134,6 +135,8 @@ public class Lower extends TreeTranslator {
|
||||
optimizeOuterThis =
|
||||
target.optimizeOuterThis() ||
|
||||
options.getBoolean("optimizeOuterThis", false);
|
||||
nullCheckOuterThis = options.getBoolean("nullCheckOuterThis",
|
||||
target.nullCheckOuterThisByDefault());
|
||||
disableProtectedAccessors = options.isSet("disableProtectedAccessors");
|
||||
Source source = Source.instance(context);
|
||||
Preview preview = Preview.instance(context);
|
||||
@ -1794,18 +1797,27 @@ public class Lower extends TreeTranslator {
|
||||
make.Ident(rhs)).setType(lhs.erasure(types)));
|
||||
}
|
||||
|
||||
/** Return tree simulating the assignment {@code this.this$n = this$n}.
|
||||
/**
|
||||
* Return tree simulating null checking outer this and/or assigning. This is
|
||||
* called when a null check is required (nullCheckOuterThis), or a synthetic
|
||||
* field is generated (stores).
|
||||
*/
|
||||
JCStatement initOuterThis(int pos, VarSymbol rhs) {
|
||||
JCStatement initOuterThis(int pos, VarSymbol rhs, boolean stores) {
|
||||
Assert.check(rhs.owner.kind == MTH);
|
||||
VarSymbol lhs = outerThisStack.head;
|
||||
Assert.check(rhs.owner.owner == lhs.owner);
|
||||
Assert.check(nullCheckOuterThis || stores); // One of the flags must be true
|
||||
make.at(pos);
|
||||
return
|
||||
make.Exec(
|
||||
make.Assign(
|
||||
JCExpression expression = make.Ident(rhs);
|
||||
if (nullCheckOuterThis) {
|
||||
expression = attr.makeNullCheck(expression);
|
||||
}
|
||||
if (stores) {
|
||||
VarSymbol lhs = outerThisStack.head;
|
||||
Assert.check(rhs.owner.owner == lhs.owner);
|
||||
expression = make.Assign(
|
||||
make.Select(make.This(lhs.owner.erasure(types)), lhs),
|
||||
make.Ident(rhs)).setType(lhs.erasure(types)));
|
||||
expression).setType(lhs.erasure(types));
|
||||
}
|
||||
return make.Exec(expression);
|
||||
}
|
||||
|
||||
/* ************************************************************************
|
||||
@ -2210,15 +2222,22 @@ public class Lower extends TreeTranslator {
|
||||
}
|
||||
// If this$n was accessed, add the field definition and prepend
|
||||
// initializer code to any super() invocation to initialize it
|
||||
if (currentClass.hasOuterInstance() && shouldEmitOuterThis(currentClass)) {
|
||||
tree.defs = tree.defs.prepend(otdef);
|
||||
enterSynthetic(tree.pos(), otdef.sym, currentClass.members());
|
||||
// otherwise prepend enclosing instance null check code if required
|
||||
emitOuter:
|
||||
if (currentClass.hasOuterInstance()) {
|
||||
boolean storesThis = shouldEmitOuterThis(currentClass);
|
||||
if (storesThis) {
|
||||
tree.defs = tree.defs.prepend(otdef);
|
||||
enterSynthetic(tree.pos(), otdef.sym, currentClass.members());
|
||||
} else if (!nullCheckOuterThis) {
|
||||
break emitOuter;
|
||||
}
|
||||
|
||||
for (JCTree def : tree.defs) {
|
||||
if (TreeInfo.isConstructor(def)) {
|
||||
JCMethodDecl mdef = (JCMethodDecl)def;
|
||||
if (TreeInfo.hasConstructorCall(mdef, names._super)) {
|
||||
List<JCStatement> initializer = List.of(initOuterThis(mdef.body.pos, mdef.params.head.sym));
|
||||
List<JCStatement> initializer = List.of(initOuterThis(mdef.body.pos, mdef.params.head.sym, storesThis)) ;
|
||||
TreeInfo.mapSuperCalls(mdef.body, supercall -> make.Block(0, initializer.append(supercall)));
|
||||
}
|
||||
}
|
||||
|
||||
@ -238,4 +238,11 @@ public enum Target {
|
||||
public boolean usesReferenceOnlySelectorTypes() {
|
||||
return compareTo(Target.JDK1_23) < 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Should we emit a null check against incoming outer this argument by default?
|
||||
*/
|
||||
public boolean nullCheckOuterThisByDefault() {
|
||||
return compareTo(JDK1_25) >= 0;
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2016, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2016, 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
|
||||
@ -59,7 +59,7 @@ public class AnnotatedExtendsTest {
|
||||
.classes(classPath.toString())
|
||||
.run()
|
||||
.getOutput(Task.OutputKind.DIRECT);
|
||||
if (!javapOut.contains("0: #21(): CLASS_EXTENDS, type_index=65535"))
|
||||
if (!javapOut.contains("0: #27(): CLASS_EXTENDS, type_index=65535"))
|
||||
throw new AssertionError("Expected output missing: " + javapOut);
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2018, 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
|
||||
@ -89,9 +89,9 @@ public class CheckNestmateAttrs {
|
||||
runCheck(params, new String [] {
|
||||
"NestHost: class CheckNestmateAttrs",
|
||||
"0: aload_0",
|
||||
"1: getfield #1 // Field this$1:LCheckNestmateAttrs$Inner;",
|
||||
"4: getfield #13 // Field CheckNestmateAttrs$Inner.this$0:LCheckNestmateAttrs;",
|
||||
"7: invokevirtual #19 // Method CheckNestmateAttrs.test:()V",
|
||||
"1: getfield #7 // Field this$1:LCheckNestmateAttrs$Inner;",
|
||||
"4: getfield #19 // Field CheckNestmateAttrs$Inner.this$0:LCheckNestmateAttrs;",
|
||||
"7: invokevirtual #25 // Method CheckNestmateAttrs.test:()V",
|
||||
"10: return"
|
||||
});
|
||||
|
||||
|
||||
@ -0,0 +1,73 @@
|
||||
/*
|
||||
* 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.InvocationTargetException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8164714
|
||||
* @summary No null check for immediate enclosing instance for VM/reflective
|
||||
* invocation of inner classes for older versions or on request
|
||||
*
|
||||
* @clean *
|
||||
* @compile -XDnullCheckOuterThis=false NoOuterThisNullChecks.java
|
||||
* @run junit NoOuterThisNullChecks
|
||||
*
|
||||
* @clean *
|
||||
* @compile --release 17 NoOuterThisNullChecks.java
|
||||
* @run junit NoOuterThisNullChecks
|
||||
*/
|
||||
class NoOuterThisNullChecks {
|
||||
static Stream<Class<?>> testClasses() {
|
||||
return Stream.of(NoOuterThis.class, OuterThisField.class);
|
||||
}
|
||||
|
||||
@MethodSource("testClasses")
|
||||
@ParameterizedTest
|
||||
void testNoOuter(Class<?> clz) {
|
||||
assertDoesNotThrow(() -> clz.getDeclaredConstructor(NoOuterThisNullChecks.class).newInstance((Object) null));
|
||||
|
||||
MethodHandle mh = assertDoesNotThrow(() -> MethodHandles.lookup().findConstructor(clz, MethodType.methodType(void.class, NoOuterThisNullChecks.class)))
|
||||
.asType(MethodType.methodType(Object.class, Object.class));
|
||||
assertDoesNotThrow(() -> {
|
||||
Object stub = mh.invokeExact((Object) null);
|
||||
});
|
||||
}
|
||||
|
||||
class NoOuterThis {}
|
||||
class OuterThisField {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "outer this = " + NoOuterThisNullChecks.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
/*
|
||||
* 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.InvocationTargetException;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import org.junit.jupiter.params.ParameterizedTest;
|
||||
import org.junit.jupiter.params.provider.MethodSource;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
/*
|
||||
* @test
|
||||
* @bug 8164714
|
||||
* @summary Null check for immediate enclosing instance for VM/reflective
|
||||
* invocation of inner classes
|
||||
*
|
||||
* @clean *
|
||||
* @compile OuterThisNullChecks.java
|
||||
* @run junit OuterThisNullChecks
|
||||
*
|
||||
* @clean *
|
||||
* @compile --release 17 -XDnullCheckOuterThis=true OuterThisNullChecks.java
|
||||
* @run junit OuterThisNullChecks
|
||||
*/
|
||||
class OuterThisNullChecks {
|
||||
static Stream<Class<?>> testClasses() {
|
||||
return Stream.of(NoOuterThis.class, OuterThisField.class);
|
||||
}
|
||||
|
||||
@MethodSource("testClasses")
|
||||
@ParameterizedTest
|
||||
void testNoOuter(Class<?> clz) {
|
||||
var ite = assertThrows(InvocationTargetException.class, () -> clz.getDeclaredConstructor(OuterThisNullChecks.class).newInstance((Object) null));
|
||||
assertInstanceOf(NullPointerException.class, ite.getCause());
|
||||
|
||||
MethodHandle mh = assertDoesNotThrow(() -> MethodHandles.lookup().findConstructor(clz, MethodType.methodType(void.class, OuterThisNullChecks.class)))
|
||||
.asType(MethodType.methodType(Object.class, Object.class));
|
||||
assertThrows(NullPointerException.class, () -> {
|
||||
Object stub = mh.invokeExact((Object) null);
|
||||
});
|
||||
}
|
||||
|
||||
class NoOuterThis {}
|
||||
class OuterThisField {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "outer this = " + OuterThisNullChecks.this;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2014, 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
|
||||
@ -49,50 +49,50 @@ public class AnnoTest {
|
||||
|
||||
expect(out,
|
||||
"RuntimeVisibleAnnotations:\n" +
|
||||
" 0: #18(#19=B#20)\n" +
|
||||
" 0: #24(#25=B#26)\n" +
|
||||
" AnnoTest$ByteAnno(\n" +
|
||||
" value=(byte) 42\n" +
|
||||
" )\n" +
|
||||
" 1: #21(#19=S#22)\n" +
|
||||
" 1: #27(#25=S#28)\n" +
|
||||
" AnnoTest$ShortAnno(\n" +
|
||||
" value=(short) 3\n" +
|
||||
" )");
|
||||
expect(out,
|
||||
"RuntimeInvisibleAnnotations:\n" +
|
||||
" 0: #24(#19=[J#25,J#27,J#29,J#31,J#33])\n" +
|
||||
" 0: #30(#25=[J#31,J#33,J#35,J#37,J#39])\n" +
|
||||
" AnnoTest$ArrayAnno(\n" +
|
||||
" value=[1l,2l,3l,4l,5l]\n" +
|
||||
" )\n" +
|
||||
" 1: #35(#19=Z#36)\n" +
|
||||
" 1: #41(#25=Z#42)\n" +
|
||||
" AnnoTest$BooleanAnno(\n" +
|
||||
" value=false\n" +
|
||||
" )\n" +
|
||||
" 2: #37(#38=c#39)\n" +
|
||||
" 2: #43(#44=c#45)\n" +
|
||||
" AnnoTest$ClassAnno(\n" +
|
||||
" type=class Ljava/lang/Object;\n" +
|
||||
" )\n" +
|
||||
" 3: #40(#41=e#42.#43)\n" +
|
||||
" 3: #46(#47=e#48.#49)\n" +
|
||||
" AnnoTest$EnumAnno(\n" +
|
||||
" kind=Ljavax/lang/model/element/ElementKind;.PACKAGE\n" +
|
||||
" )\n" +
|
||||
" 4: #44(#19=I#45)\n" +
|
||||
" 4: #50(#25=I#51)\n" +
|
||||
" AnnoTest$IntAnno(\n" +
|
||||
" value=2\n" +
|
||||
" )\n" +
|
||||
" 5: #46()\n" +
|
||||
" 5: #52()\n" +
|
||||
" AnnoTest$IntDefaultAnno\n" +
|
||||
" 6: #47(#48=s#49)\n" +
|
||||
" 6: #53(#54=s#55)\n" +
|
||||
" AnnoTest$NameAnno(\n" +
|
||||
" name=\"NAME\"\n" +
|
||||
" )\n" +
|
||||
" 7: #50(#51=D#52,#54=F#55)\n" +
|
||||
" 7: #56(#57=D#58,#60=F#61)\n" +
|
||||
" AnnoTest$MultiAnno(\n" +
|
||||
" d=3.14159d\n" +
|
||||
" f=2.71828f\n" +
|
||||
" )\n" +
|
||||
" 8: #56()\n" +
|
||||
" 8: #62()\n" +
|
||||
" AnnoTest$SimpleAnno\n" +
|
||||
" 9: #57(#19=@#44(#19=I#58))\n" +
|
||||
" 9: #63(#25=@#50(#25=I#64))\n" +
|
||||
" AnnoTest$AnnoAnno(\n" +
|
||||
" value=@AnnoTest$IntAnno(\n" +
|
||||
" value=5\n" +
|
||||
@ -100,7 +100,7 @@ public class AnnoTest {
|
||||
" )");
|
||||
expect(out,
|
||||
"RuntimeInvisibleTypeAnnotations:\n" +
|
||||
" 0: #60(): CLASS_EXTENDS, type_index=0\n" +
|
||||
" 0: #66(): CLASS_EXTENDS, type_index=0\n" +
|
||||
" AnnoTest$TypeAnno");
|
||||
|
||||
if (errors > 0)
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright (c) 2009, 2024, Oracle and/or its affiliates. All rights reserved.
|
||||
* Copyright (c) 2009, 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
|
||||
@ -57,7 +57,8 @@ public class T6887895 {
|
||||
"java/lang/Object",
|
||||
"java/lang/String",
|
||||
"T6887895",
|
||||
"T6887895$Test"
|
||||
"T6887895$Test",
|
||||
"java/util/Objects",
|
||||
};
|
||||
|
||||
Set<String> expect = new TreeSet<String>(Arrays.asList(expectNames));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user