Skip to content

Commit 219e600

Browse files
committed
[GR-40323] Add MethodHandles.Lookup parameter to Shape.Builder.layout().
PullRequest: graal/19451
2 parents 0046d69 + 334c870 commit 219e600

File tree

19 files changed

+313
-91
lines changed

19 files changed

+313
-91
lines changed

compiler/src/jdk.graal.compiler.test/src/jdk/graal/compiler/truffle/test/DynamicObjectPartialEvaluationTest.java

+18-15
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2022, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -24,18 +24,8 @@
2424
*/
2525
package jdk.graal.compiler.truffle.test;
2626

27-
import jdk.graal.compiler.truffle.test.nodes.AbstractTestNode;
28-
import jdk.graal.compiler.truffle.test.nodes.RootTestNode;
29-
import jdk.graal.compiler.graph.Node;
30-
import jdk.graal.compiler.nodes.NamedLocationIdentity;
31-
import jdk.graal.compiler.nodes.StructuredGraph;
32-
import jdk.graal.compiler.nodes.extended.GuardedUnsafeLoadNode;
33-
import jdk.graal.compiler.nodes.extended.RawLoadNode;
34-
import jdk.graal.compiler.nodes.extended.RawStoreNode;
35-
import jdk.graal.compiler.nodes.extended.UnsafeAccessNode;
36-
import jdk.graal.compiler.nodes.java.LoadFieldNode;
37-
import jdk.graal.compiler.nodes.java.StoreFieldNode;
38-
import jdk.graal.compiler.truffle.nodes.ObjectLocationIdentity;
27+
import java.lang.invoke.MethodHandles;
28+
3929
import org.junit.Assert;
4030
import org.junit.Before;
4131
import org.junit.Test;
@@ -50,6 +40,18 @@
5040
import com.oracle.truffle.api.object.Shape;
5141
import com.oracle.truffle.runtime.OptimizedCallTarget;
5242

43+
import jdk.graal.compiler.graph.Node;
44+
import jdk.graal.compiler.nodes.NamedLocationIdentity;
45+
import jdk.graal.compiler.nodes.StructuredGraph;
46+
import jdk.graal.compiler.nodes.extended.GuardedUnsafeLoadNode;
47+
import jdk.graal.compiler.nodes.extended.RawLoadNode;
48+
import jdk.graal.compiler.nodes.extended.RawStoreNode;
49+
import jdk.graal.compiler.nodes.extended.UnsafeAccessNode;
50+
import jdk.graal.compiler.nodes.java.LoadFieldNode;
51+
import jdk.graal.compiler.nodes.java.StoreFieldNode;
52+
import jdk.graal.compiler.truffle.nodes.ObjectLocationIdentity;
53+
import jdk.graal.compiler.truffle.test.nodes.AbstractTestNode;
54+
import jdk.graal.compiler.truffle.test.nodes.RootTestNode;
5355
import jdk.vm.ci.meta.JavaKind;
5456

5557
public class DynamicObjectPartialEvaluationTest extends PartialEvaluationTest {
@@ -59,8 +61,9 @@ public class DynamicObjectPartialEvaluationTest extends PartialEvaluationTest {
5961

6062
@Before
6163
public void before() {
62-
rootShapeWithoutFields = Shape.newBuilder().layout(TestDynamicObject.class).build();
63-
rootShapeWithFields = Shape.newBuilder().layout(TestDynamicObjectWithFields.class).build();
64+
var lookup = MethodHandles.lookup();
65+
rootShapeWithoutFields = Shape.newBuilder().layout(TestDynamicObject.class, lookup).build();
66+
rootShapeWithFields = Shape.newBuilder().layout(TestDynamicObjectWithFields.class, lookup).build();
6467
newInstanceWithFields();
6568
newInstanceWithoutFields();
6669
}

substratevm/src/com.oracle.svm.truffle/src/com/oracle/svm/truffle/TruffleBaseFeature.java

+10-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved.
2+
* Copyright (c) 2013, 2024, Oracle and/or its affiliates. All rights reserved.
33
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
44
*
55
* This code is free software; you can redistribute it and/or modify it
@@ -32,6 +32,7 @@
3232
import java.io.InputStream;
3333
import java.io.InputStreamReader;
3434
import java.lang.annotation.Annotation;
35+
import java.lang.invoke.MethodHandles;
3536
import java.lang.reflect.Constructor;
3637
import java.lang.reflect.Field;
3738
import java.lang.reflect.InvocationTargetException;
@@ -857,7 +858,14 @@ private void initializeDynamicObjectLayouts(AnalysisType type) {
857858

858859
private static void initializeDynamicObjectLayoutImpl(Class<?> javaClass) {
859860
// Initialize DynamicObject layout info for every instantiated DynamicObject subclass.
860-
invokeStaticMethod("com.oracle.truffle.object.LayoutImpl", "initializeDynamicObjectLayout", Collections.singleton(Class.class), javaClass);
861+
MethodHandles.Lookup trustedLookup = (MethodHandles.Lookup) ReflectionUtil.readStaticField(MethodHandles.Lookup.class, "IMPL_LOOKUP");
862+
MethodHandles.Lookup privateLookup;
863+
try {
864+
privateLookup = MethodHandles.privateLookupIn(javaClass, trustedLookup);
865+
} catch (IllegalAccessException e) {
866+
throw VMError.shouldNotReachHere(e);
867+
}
868+
invokeStaticMethod("com.oracle.truffle.object.LayoutImpl", "initializeDynamicObjectLayout", List.of(Class.class, MethodHandles.Lookup.class), javaClass, privateLookup);
861869
}
862870

863871
private static void registerDynamicObjectFields(BeforeAnalysisAccessImpl config) {

truffle/CHANGELOG.md

+1
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ This changelog summarizes major changes between Truffle versions relevant to lan
2121
* GR-55296 Added support for creation of strings from raw byte arrays and native memory using
2222
`Value.fromByteBasedString(...)` `Value.fromNativeString(...)`. A `Value.StringEncoding` must be provided.
2323
* GR-55296 Added support to convert any string to a `byte[]` with a given `Value.StringEncoding` using `Value.asStringBytes(...)`.
24+
* GR-40323 Deprecated `Shape.Builder.layout(Class)` for removal and added replacement API [`Shape.Builder.layout(Class, MethodHandles.Lookup)`](https://www.graalvm.org/truffle/javadoc/com/oracle/truffle/api/object/Shape.Builder.html#layout(java.lang.Class,java.lang.MethodHandles.Lookup)). Replace usages with the new method, additionally providing a `Lookup` that has full privilege access to the layout class or the module in which it is declared, as obtained by `MethodHandles.lookup()`. See javadoc for the updated usage.
2425

2526

2627
* GR-54760 `RootNode.translateStackTraceElement()` is now always consulted for polyglot and debugger stack traces. Stack traces now use the source section, the executable name, and the name of the declared meta-object to build `StackTraceElement` instances.

truffle/src/com.oracle.truffle.api.object/snapshot.sigtest

+5-2
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,7 @@ intf com.oracle.truffle.api.interop.TruffleObject
107107
meth protected java.lang.Object clone() throws java.lang.CloneNotSupportedException
108108
meth public final com.oracle.truffle.api.object.Shape getShape()
109109
supr java.lang.Object
110-
hfds SHAPE_OFFSET,UNSAFE,extRef,extVal,shape
110+
hfds LOOKUP,SHAPE_OFFSET,UNSAFE,extRef,extVal,shape
111111

112112
CLSS protected abstract interface static !annotation com.oracle.truffle.api.object.DynamicObject$DynamicField
113113
outer com.oracle.truffle.api.object.DynamicObject
@@ -298,6 +298,7 @@ cons protected init()
298298
innr public final static Builder
299299
innr public final static DerivedBuilder
300300
meth protected abstract com.oracle.truffle.api.object.Layout getLayout()
301+
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="24.2")
301302
meth protected abstract com.oracle.truffle.api.object.Shape addProperty(com.oracle.truffle.api.object.Property)
302303
meth protected abstract com.oracle.truffle.api.object.Shape defineProperty(java.lang.Object,java.lang.Object,int,int)
303304
meth protected abstract com.oracle.truffle.api.object.Shape removeProperty(com.oracle.truffle.api.object.Property)
@@ -346,13 +347,15 @@ meth public com.oracle.truffle.api.object.Shape$Builder allowImplicitCastIntToDo
346347
meth public com.oracle.truffle.api.object.Shape$Builder allowImplicitCastIntToLong(boolean)
347348
meth public com.oracle.truffle.api.object.Shape$Builder dynamicType(java.lang.Object)
348349
meth public com.oracle.truffle.api.object.Shape$Builder layout(java.lang.Class<? extends com.oracle.truffle.api.object.DynamicObject>)
350+
anno 0 java.lang.Deprecated(boolean forRemoval=false, java.lang.String since="24.2")
351+
meth public com.oracle.truffle.api.object.Shape$Builder layout(java.lang.Class<? extends com.oracle.truffle.api.object.DynamicObject>,java.lang.invoke.MethodHandles$Lookup)
349352
meth public com.oracle.truffle.api.object.Shape$Builder propertyAssumptions(boolean)
350353
meth public com.oracle.truffle.api.object.Shape$Builder shapeFlags(int)
351354
meth public com.oracle.truffle.api.object.Shape$Builder shared(boolean)
352355
meth public com.oracle.truffle.api.object.Shape$Builder sharedData(java.lang.Object)
353356
meth public com.oracle.truffle.api.object.Shape$Builder singleContextAssumption(com.oracle.truffle.api.Assumption)
354357
supr java.lang.Object<com.oracle.truffle.api.object.Shape$Builder>
355-
hfds allowImplicitCastIntToDouble,allowImplicitCastIntToLong,dynamicType,layoutClass,properties,propertyAssumptions,shapeFlags,shared,sharedData,singleContextAssumption
358+
hfds allowImplicitCastIntToDouble,allowImplicitCastIntToLong,dynamicType,layoutClass,layoutLookup,properties,propertyAssumptions,shapeFlags,shared,sharedData,singleContextAssumption
356359

357360
CLSS public final static com.oracle.truffle.api.object.Shape$DerivedBuilder
358361
outer com.oracle.truffle.api.object.Shape

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/DynamicObject.java

+15-7
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@
4545
import java.lang.annotation.Retention;
4646
import java.lang.annotation.RetentionPolicy;
4747
import java.lang.annotation.Target;
48+
import java.lang.invoke.MethodHandles;
49+
import java.lang.invoke.MethodHandles.Lookup;
4850
import java.lang.reflect.Field;
4951

5052
import com.oracle.truffle.api.CompilerDirectives;
@@ -65,7 +67,7 @@
6567
* shape caches to be shared across contexts.
6668
*
6769
* Subclasses can provide in-object dynamic field slots using the {@link DynamicField} annotation
68-
* and {@link Shape.Builder#layout(Class) Shape.Builder.layout}.
70+
* and {@link Shape.Builder#layout(Class, Lookup) Shape.Builder.layout}.
6971
*
7072
* <p>
7173
* Example:
@@ -92,12 +94,14 @@
9294
*/
9395
public abstract class DynamicObject implements TruffleObject {
9496

97+
private static final MethodHandles.Lookup LOOKUP = internalLookup();
98+
9599
/**
96100
* Using this annotation, subclasses can define additional dynamic fields to be used by the
97101
* object layout. Annotated field must be of type {@code Object} or {@code long}, must not be
98102
* final, and must not have any direct usages.
99103
*
100-
* @see Shape.Builder#layout(Class)
104+
* @see Shape.Builder#layout(Class, Lookup)
101105
* @since 20.2.0
102106
*/
103107
@Retention(RetentionPolicy.RUNTIME)
@@ -115,10 +119,10 @@ public abstract class DynamicObject implements TruffleObject {
115119

116120
/**
117121
* Constructor for {@link DynamicObject} subclasses. Initializes the object with the provided
118-
* shape. The shape must have been constructed with a {@linkplain Shape.Builder#layout(Class)
119-
* layout class} assignable from this class (i.e., the concrete subclass, a superclass thereof,
120-
* including {@link DynamicObject}) and must not have any instance properties (but may have
121-
* constant properties).
122+
* shape. The shape must have been constructed with a
123+
* {@linkplain Shape.Builder#layout(Class, Lookup) layout class} assignable from this class
124+
* (i.e., the concrete subclass, a superclass thereof, including {@link DynamicObject}) and must
125+
* not have any instance properties (but may have constant properties).
122126
*
123127
* <p>
124128
* Examples:
@@ -129,7 +133,7 @@ public abstract class DynamicObject implements TruffleObject {
129133
* </pre>
130134
*
131135
* <pre>
132-
* Shape shape = {@link Shape#newBuilder()}.{@link Shape.Builder#layout(Class) layout}(MyObject.class).{@link Shape.Builder#build() build}();
136+
* Shape shape = {@link Shape#newBuilder()}.{@link Shape.Builder#layout(Class, Lookup) layout}(MyObject.class, MethodHandles.lookup()).{@link Shape.Builder#build() build}();
133137
* DynamicObject myObject = new MyObject(shape);
134138
* </pre>
135139
*
@@ -253,6 +257,10 @@ static Class<? extends Annotation> getDynamicFieldAnnotation() {
253257
return DynamicField.class;
254258
}
255259

260+
static Lookup internalLookup() {
261+
return LOOKUP;
262+
}
263+
256264
private static final Unsafe UNSAFE;
257265
private static final long SHAPE_OFFSET;
258266
static {

truffle/src/com.oracle.truffle.api.object/src/com/oracle/truffle/api/object/Shape.java

+96-5
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@
4040
*/
4141
package com.oracle.truffle.api.object;
4242

43+
import java.lang.invoke.MethodHandles;
44+
import java.lang.invoke.MethodHandles.Lookup;
4345
import java.util.List;
4446
import java.util.Objects;
4547
import java.util.function.Predicate;
@@ -168,6 +170,7 @@ static int checkShapeFlags(int flags) {
168170
public static final class Builder extends AbstractBuilder<Builder> {
169171

170172
private Class<? extends DynamicObject> layoutClass = DynamicObject.class;
173+
private MethodHandles.Lookup layoutLookup = DynamicObject.internalLookup();
171174
private Object dynamicType = ObjectType.DEFAULT;
172175
private int shapeFlags;
173176
private boolean allowImplicitCastIntToDouble;
@@ -212,14 +215,95 @@ public static final class Builder extends AbstractBuilder<Builder> {
212215
*
213216
* @param layoutClass custom object layout class
214217
* @since 20.2.0
218+
* @deprecated Use {@link #layout(Class, MethodHandles.Lookup)} instead.
215219
*/
220+
@Deprecated(since = "24.2")
216221
public Builder layout(Class<? extends DynamicObject> layoutClass) {
217222
CompilerAsserts.neverPartOfCompilation();
218223
if (!DynamicObject.class.isAssignableFrom(layoutClass)) {
219224
throw new IllegalArgumentException(String.format("Expected a subclass of %s but got: %s",
220225
DynamicObject.class.getName(), layoutClass.getTypeName()));
221226
}
222227
this.layoutClass = layoutClass;
228+
this.layoutLookup = null;
229+
return this;
230+
}
231+
232+
/**
233+
* Sets a custom object layout class (default: <code>{@link DynamicObject}.class</code>) and
234+
* a corresponding {@link MethodHandles.Lookup} created by the layout class, or a class in
235+
* the same module, that has full privilege access in order to provide access to its fields.
236+
* <p>
237+
* Enables the allocation of any additional {@code DynamicField}-annotated fields declared
238+
* in the {@link DynamicObject} layout subclass as storage locations. By default, only
239+
* extension array based storage locations in the {@link DynamicObject} base class are used.
240+
* <p>
241+
* Also restricts the shape to a specific {@link DynamicObject} subclass and subclasses
242+
* thereof, i.e. shapes created for a {@link DynamicObject} layout subclass, and any derived
243+
* shapes, can only be used to instantiate, and be assigned to, instances of that class
244+
* (regardless of whether the class actually contains any dynamic fields).
245+
*
246+
* <p>
247+
* Example:
248+
*
249+
* <pre>
250+
* <code>
251+
* public class MyObject extends DynamicObject implements TruffleObject {
252+
*
253+
* &#64;DynamicField private Object _obj1;
254+
* &#64;DynamicField private Object _obj2;
255+
* &#64;DynamicField private long _long1;
256+
* &#64;DynamicField private long _long2;
257+
*
258+
* public MyObject(Shape shape) {
259+
* super(shape);
260+
* }
261+
*
262+
* static MethodHandles.Lookup lookup() {
263+
* return MethodHandles.lookup();
264+
* }
265+
* }
266+
*
267+
*
268+
* Shape myObjShape = Shape.newBuilder().layout(MyObject.class, MyObject.lookup()).build();
269+
* MyObject obj = new MyObject(myObjShape);
270+
* </code>
271+
* </pre>
272+
*
273+
* @param layoutClass a {@link DynamicObject} layout subclass or the default
274+
* <code>{@link DynamicObject}.class</code>.
275+
* @param layoutClassLookup a {@link Lookup} that has full privilege access, created using
276+
* {@link MethodHandles#lookup()}, either by the layout class itself, or a class
277+
* in the same module. If <code>layoutClass == DynamicObject.class</code>, and
278+
* only then, this parameter is ignored and may be <code>null</code>.
279+
* @throws IllegalArgumentException if <code>layoutClass != DynamicObject.class</code> and
280+
* the lookup does not have {@link MethodHandles.Lookup#hasFullPrivilegeAccess()
281+
* full privilege access} or is not from the layout class or a class within the
282+
* same module as the layout class.
283+
* @throws NullPointerException if <code>layoutClass == null</code>, or
284+
* <code>lookup == null</code> and
285+
* <code>layoutClass != {@link DynamicObject}.class</code>.
286+
* @since 24.2.0
287+
*/
288+
public Builder layout(Class<? extends DynamicObject> layoutClass, Lookup layoutClassLookup) {
289+
CompilerAsserts.neverPartOfCompilation();
290+
if (!DynamicObject.class.isAssignableFrom(layoutClass)) {
291+
throw new IllegalArgumentException(String.format("Expected a subclass of %s but got: %s",
292+
DynamicObject.class.getName(), layoutClass.getTypeName()));
293+
}
294+
if (layoutClass == DynamicObject.class) {
295+
this.layoutClass = DynamicObject.class;
296+
this.layoutLookup = DynamicObject.internalLookup();
297+
return this;
298+
}
299+
if (!layoutClassLookup.hasFullPrivilegeAccess()) {
300+
throw new IllegalArgumentException("Lookup must have full privilege access");
301+
}
302+
if (layoutClassLookup.lookupClass().getModule() != layoutClass.getModule()) {
303+
throw new IllegalArgumentException("Lookup must be from the same module as the layout class");
304+
}
305+
this.layoutClass = layoutClass;
306+
this.layoutLookup = layoutClassLookup;
223307
return this;
224308
}
225309

@@ -343,7 +427,6 @@ public Builder addConstantProperty(Object key, Object value, int flags) {
343427
* Allows values to be implicitly cast from int to long in this shape and any derived
344428
* shapes.
345429
*
346-
* @see #layout(Class)
347430
* @since 20.2.0
348431
*/
349432
public Builder allowImplicitCastIntToLong(boolean allow) {
@@ -355,7 +438,6 @@ public Builder allowImplicitCastIntToLong(boolean allow) {
355438
* Allows values to be implicitly cast from int to double in this shape and any derived
356439
* shapes.
357440
*
358-
* @see #layout(Class)
359441
* @since 20.2.0
360442
*/
361443
public Builder allowImplicitCastIntToDouble(boolean allow) {
@@ -379,7 +461,15 @@ public Shape build() {
379461
}
380462

381463
int implicitCastFlags = (allowImplicitCastIntToDouble ? Layout.INT_TO_DOUBLE_FLAG : 0) | (allowImplicitCastIntToLong ? Layout.INT_TO_LONG_FLAG : 0);
382-
Shape shape = Layout.getFactory().createShape(new Object[]{layoutClass, implicitCastFlags, dynamicType, sharedData, flags, properties, singleContextAssumption});
464+
Shape shape = Layout.getFactory().createShape(new Object[]{
465+
layoutClass,
466+
implicitCastFlags,
467+
dynamicType,
468+
sharedData,
469+
flags,
470+
properties,
471+
singleContextAssumption,
472+
layoutLookup});
383473

384474
assert shape.isShared() == shared && shape.getFlags() == shapeFlags && shape.getDynamicType() == dynamicType;
385475
return shape;
@@ -745,16 +835,17 @@ protected Shape setDynamicType(Object dynamicType) {
745835
/**
746836
* Get the shape's layout.
747837
*
748-
* @see Shape.Builder#layout(Class)
749838
* @since 0.8 or earlier
839+
* @deprecated No replacement. Use {@link #getLayoutClass()} to get the layout class.
750840
*/
841+
@Deprecated(since = "24.2")
751842
@SuppressWarnings("deprecation")
752843
protected abstract Layout getLayout();
753844

754845
/**
755846
* Get the shape's layout class.
756847
*
757-
* @see Shape.Builder#layout(Class)
848+
* @see Shape.Builder#layout(Class, Lookup)
758849
* @since 21.1
759850
*/
760851
@SuppressWarnings("deprecation")

0 commit comments

Comments
 (0)