mirror of
https://github.com/apple/pkl.git
synced 2026-05-25 16:19:20 +02:00
Move to truffle object model
This commit is contained in:
@@ -55,8 +55,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
|||||||
|
|
||||||
@get:Inject protected abstract val execOperations: ExecOperations
|
@get:Inject protected abstract val execOperations: ExecOperations
|
||||||
|
|
||||||
private val graalVm: Provider<BuildInfo.GraalVm> =
|
private val graalVm: Provider<BuildInfo.GraalVm> = arch.map { a ->
|
||||||
arch.map { a ->
|
|
||||||
when (a) {
|
when (a) {
|
||||||
Architecture.AMD64 -> buildInfo.graalVmAmd64
|
Architecture.AMD64 -> buildInfo.graalVmAmd64
|
||||||
Architecture.AARCH64 -> buildInfo.graalVmAarch64
|
Architecture.AARCH64 -> buildInfo.graalVmAarch64
|
||||||
@@ -132,7 +131,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
|||||||
add(imageName.get())
|
add(imageName.get())
|
||||||
// the actual limit (currently) used by native-image is this number + 1400 (idea is to
|
// the actual limit (currently) used by native-image is this number + 1400 (idea is to
|
||||||
// compensate for Truffle's own nodes)
|
// compensate for Truffle's own nodes)
|
||||||
add("-H:MaxRuntimeCompileMethods=1800")
|
add("-H:MaxRuntimeCompileMethods=2000")
|
||||||
add("-H:+EnforceMaxRuntimeCompileMethods")
|
add("-H:+EnforceMaxRuntimeCompileMethods")
|
||||||
add("--enable-url-protocols=http,https")
|
add("--enable-url-protocols=http,https")
|
||||||
add("-H:+ReportExceptionStackTraces")
|
add("-H:+ReportExceptionStackTraces")
|
||||||
@@ -151,8 +150,7 @@ abstract class NativeImageBuild : DefaultTask() {
|
|||||||
}
|
}
|
||||||
// native-image rejects non-existing class path entries -> filter
|
// native-image rejects non-existing class path entries -> filter
|
||||||
add("--class-path")
|
add("--class-path")
|
||||||
val pathInput =
|
val pathInput = classpath.filter {
|
||||||
classpath.filter {
|
|
||||||
it.exists() && !exclusions.any { exclude -> it.name.contains(exclude) }
|
it.exists() && !exclusions.any { exclude -> it.name.contains(exclude) }
|
||||||
}
|
}
|
||||||
add(pathInput.asPath)
|
add(pathInput.asPath)
|
||||||
|
|||||||
+19
-7
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,10 +22,20 @@ import com.oracle.truffle.api.nodes.ExplodeLoop;
|
|||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
import org.pkl.core.runtime.VmObjectLike;
|
import org.pkl.core.runtime.VmObject;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
|
||||||
/** Reads a local non-constant property that is known to exist in the lexical scope of this node. */
|
/**
|
||||||
|
* Reads a local non-constant property that is known to exist in the lexical scope of this node.
|
||||||
|
*
|
||||||
|
* <p>Local property values are cached using the ObjectMember as the key (identity-based) rather
|
||||||
|
* than the property name. This is necessary because the same property name can exist at different
|
||||||
|
* declaration sites in an amends chain, and we need to distinguish between them for correct
|
||||||
|
* late-binding semantics.
|
||||||
|
*
|
||||||
|
* <p>The cache is stored in a separate IdentityHashMap in VmObject (not in the DynamicObject
|
||||||
|
* storage) to avoid shape transitions that would destroy cache locality.
|
||||||
|
*/
|
||||||
public final class ReadLocalPropertyNode extends ExpressionNode {
|
public final class ReadLocalPropertyNode extends ExpressionNode {
|
||||||
private final ObjectMember property;
|
private final ObjectMember property;
|
||||||
private final int levelsUp;
|
private final int levelsUp;
|
||||||
@@ -63,15 +73,17 @@ public final class ReadLocalPropertyNode extends ExpressionNode {
|
|||||||
owner = owner.getEnclosingOwner();
|
owner = owner.getEnclosingOwner();
|
||||||
}
|
}
|
||||||
|
|
||||||
assert receiver instanceof VmObjectLike
|
assert receiver instanceof VmObject
|
||||||
: "Assumption: This node isn't used in Truffle ASTs of `external` pkl.base classes whose values aren't VmObject's.";
|
: "Assumption: This node isn't used in Truffle ASTs of `external` pkl.base classes whose values aren't VmObject's.";
|
||||||
|
|
||||||
var objReceiver = (VmObjectLike) receiver;
|
// Use the local property cache instead of DynamicObject storage
|
||||||
var result = objReceiver.getCachedValue(property);
|
// to avoid shape transitions from ObjectMember keys
|
||||||
|
var objReceiver = (VmObject) receiver;
|
||||||
|
var result = objReceiver.getLocalCachedValue(property);
|
||||||
|
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = callNode.call(objReceiver, owner, property.getName());
|
result = callNode.call(objReceiver, owner, property.getName());
|
||||||
objReceiver.setCachedValue(property, result);
|
objReceiver.setLocalCachedValue(property, result);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -19,9 +19,12 @@ import com.oracle.truffle.api.CompilerDirectives;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.dsl.*;
|
import com.oracle.truffle.api.dsl.*;
|
||||||
|
import com.oracle.truffle.api.dsl.Cached.Shared;
|
||||||
|
import com.oracle.truffle.api.library.CachedLibrary;
|
||||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObjectLibrary;
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.MemberLookupMode;
|
import org.pkl.core.ast.MemberLookupMode;
|
||||||
@@ -61,6 +64,27 @@ public abstract class ReadPropertyNode extends ExpressionNode {
|
|||||||
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
|
this(sourceSection, propertyName, MemberLookupMode.EXPLICIT_RECEIVER, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Optimized specialization for VmTyped using DynamicObjectLibrary
|
||||||
|
@Specialization
|
||||||
|
protected Object evalTyped(
|
||||||
|
VmTyped receiver,
|
||||||
|
@CachedLibrary(limit = "3") DynamicObjectLibrary objectLibrary,
|
||||||
|
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
|
||||||
|
|
||||||
|
checkConst(receiver);
|
||||||
|
|
||||||
|
// fast path: check cache using optimized library access
|
||||||
|
var result = receiver.getCachedValue(propertyName, objectLibrary);
|
||||||
|
if (result != null) return result;
|
||||||
|
|
||||||
|
// slow path: look up member in prototype chain and compute value
|
||||||
|
result = VmUtils.readMemberOrNull(receiver, propertyName, true, callNode);
|
||||||
|
if (result != null) return result;
|
||||||
|
|
||||||
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
throw cannotFindProperty(receiver);
|
||||||
|
}
|
||||||
|
|
||||||
// This method effectively covers `VmObject receiver` but is implemented in a more
|
// This method effectively covers `VmObject receiver` but is implemented in a more
|
||||||
// efficient way. See:
|
// efficient way. See:
|
||||||
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces
|
// https://www.graalvm.org/22.0/graalvm-as-a-platform/language-implementation-framework/TruffleLibraries/#strategy-2-java-interfaces
|
||||||
@@ -68,7 +92,7 @@ public abstract class ReadPropertyNode extends ExpressionNode {
|
|||||||
protected Object evalObject(
|
protected Object evalObject(
|
||||||
Object receiver,
|
Object receiver,
|
||||||
@Cached("getVmObjectSubclassOrNull(receiver)") Class<? extends VmObjectLike> cachedClass,
|
@Cached("getVmObjectSubclassOrNull(receiver)") Class<? extends VmObjectLike> cachedClass,
|
||||||
@Cached("create()") IndirectCallNode callNode) {
|
@Cached("create()") @Shared("callNode") IndirectCallNode callNode) {
|
||||||
|
|
||||||
var object = cachedClass.cast(receiver);
|
var object = cachedClass.cast(receiver);
|
||||||
checkConst(object);
|
checkConst(object);
|
||||||
|
|||||||
@@ -0,0 +1,38 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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 org.pkl.core.runtime;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.object.Shape;
|
||||||
|
|
||||||
|
/** Factory for Truffle {@link Shape} instances used by Pkl objects. */
|
||||||
|
public final class PklShape {
|
||||||
|
|
||||||
|
/** The root shape for all Pkl object instances. */
|
||||||
|
private static final Shape ROOT_SHAPE = Shape.newBuilder().build();
|
||||||
|
|
||||||
|
private PklShape() {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the root shape for Pkl objects.
|
||||||
|
*
|
||||||
|
* <p>This is the base shape from which all instance shapes derive. Properties are added
|
||||||
|
* dynamically as values are cached via {@link
|
||||||
|
* com.oracle.truffle.api.object.DynamicObjectLibrary#put}.
|
||||||
|
*/
|
||||||
|
public static Shape getRootShape() {
|
||||||
|
return ROOT_SHAPE;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -104,7 +104,7 @@ public final class TestRunner {
|
|||||||
if (factValue == Boolean.FALSE) {
|
if (factValue == Boolean.FALSE) {
|
||||||
if (PowerAssertions.isEnabled()) {
|
if (PowerAssertions.isEnabled()) {
|
||||||
try (var valueTracker = valueTrackerFactory.create()) {
|
try (var valueTracker = valueTrackerFactory.create()) {
|
||||||
listing.cachedValues.clear();
|
listing.cleanAllCachedValues();
|
||||||
VmUtils.readMember(listing, idx);
|
VmUtils.readMember(listing, idx);
|
||||||
var failure =
|
var failure =
|
||||||
factFailure(
|
factFailure(
|
||||||
|
|||||||
@@ -29,7 +29,7 @@ import org.pkl.core.util.ByteArrayUtils;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmBytes extends VmValue implements Iterable<Long> {
|
public final class VmBytes implements VmValue, Iterable<Long> {
|
||||||
|
|
||||||
private @Nullable VmList vmList;
|
private @Nullable VmList vmList;
|
||||||
private @Nullable String base64;
|
private @Nullable String base64;
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.dsl.Idempotent;
|
import com.oracle.truffle.api.dsl.Idempotent;
|
||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
|
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||||
|
import com.oracle.truffle.api.object.Shape;
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.*;
|
import java.util.function.*;
|
||||||
@@ -43,7 +45,7 @@ import org.pkl.core.util.Nullable;
|
|||||||
// The currently implemented (and likely insufficient) solution is to
|
// The currently implemented (and likely insufficient) solution is to
|
||||||
// * deeply force standard library modules at initialization time.
|
// * deeply force standard library modules at initialization time.
|
||||||
// * ensure that any further mutation (e.g., lazy initialization in VmClass) is thread-safe.
|
// * ensure that any further mutation (e.g., lazy initialization in VmClass) is thread-safe.
|
||||||
public final class VmClass extends VmValue {
|
public final class VmClass implements VmValue {
|
||||||
private final SourceSection sourceSection;
|
private final SourceSection sourceSection;
|
||||||
private final SourceSection headerSection;
|
private final SourceSection headerSection;
|
||||||
private final SourceSection @Nullable [] docComment;
|
private final SourceSection @Nullable [] docComment;
|
||||||
@@ -123,6 +125,16 @@ public final class VmClass extends VmValue {
|
|||||||
|
|
||||||
private final Object mapToTypedMembersLock = new Object();
|
private final Object mapToTypedMembersLock = new Object();
|
||||||
|
|
||||||
|
// Shape for instances of this class - used for Truffle's Dynamic Object Model
|
||||||
|
@LateInit
|
||||||
|
@GuardedBy("instanceShapeLock")
|
||||||
|
private Shape __instanceShape;
|
||||||
|
|
||||||
|
private final Object instanceShapeLock = new Object();
|
||||||
|
|
||||||
|
/** Cached IndirectCallNode for force() operations. */
|
||||||
|
private final IndirectCallNode cachedCallNode = IndirectCallNode.create();
|
||||||
|
|
||||||
public VmClass(
|
public VmClass(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
SourceSection headerSection,
|
SourceSection headerSection,
|
||||||
@@ -697,6 +709,41 @@ public final class VmClass extends VmValue {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Truffle Shape for instances of this class.
|
||||||
|
*
|
||||||
|
* <p>The shape is lazily initialized from the root shape. Instance shapes are used by the Dynamic
|
||||||
|
* Object Model to provide optimized property storage and inline caching for cached property
|
||||||
|
* values.
|
||||||
|
*/
|
||||||
|
public Shape getInstanceShape() {
|
||||||
|
synchronized (instanceShapeLock) {
|
||||||
|
if (__instanceShape == null) {
|
||||||
|
__instanceShape = buildInstanceShape();
|
||||||
|
}
|
||||||
|
return __instanceShape;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@TruffleBoundary
|
||||||
|
private Shape buildInstanceShape() {
|
||||||
|
// start with the superclass shape if available, otherwise use root shape
|
||||||
|
if (superclass != null) {
|
||||||
|
return superclass.getInstanceShape();
|
||||||
|
}
|
||||||
|
return PklShape.getRootShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the cached IndirectCallNode for force() operations.
|
||||||
|
*
|
||||||
|
* <p>This node is shared by all instances of this class and avoids the overhead of {@code
|
||||||
|
* IndirectCallNode.getUncached()} which performs a lookup on every call.
|
||||||
|
*/
|
||||||
|
public IndirectCallNode getCachedCallNode() {
|
||||||
|
return cachedCallNode;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tells if the given property defines a member of this class. Requires a fully initialized
|
* Tells if the given property defines a member of this class. Requires a fully initialized
|
||||||
* inheritance hierarchy.
|
* inheritance hierarchy.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,7 +20,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import org.organicdesign.fp.xform.Xform;
|
import org.organicdesign.fp.xform.Xform;
|
||||||
|
|
||||||
public abstract class VmCollection extends VmValue implements Iterable<Object> {
|
public abstract class VmCollection implements VmValue, Iterable<Object> {
|
||||||
public interface Builder<T extends VmCollection> {
|
public interface Builder<T extends VmCollection> {
|
||||||
void add(Object element);
|
void add(Object element);
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -26,7 +26,7 @@ import org.pkl.core.util.MathUtils;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmDataSize extends VmValue implements Comparable<VmDataSize> {
|
public final class VmDataSize implements VmValue, Comparable<VmDataSize> {
|
||||||
private static final Map<Identifier, DataSizeUnit> UNITS =
|
private static final Map<Identifier, DataSizeUnit> UNITS =
|
||||||
Map.ofEntries(
|
Map.ofEntries(
|
||||||
entry(Identifier.B, DataSizeUnit.BYTES),
|
entry(Identifier.B, DataSizeUnit.BYTES),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -22,7 +22,7 @@ import org.pkl.core.util.DurationUtils;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmDuration extends VmValue implements Comparable<VmDuration> {
|
public final class VmDuration implements VmValue, Comparable<VmDuration> {
|
||||||
private static final Map<Identifier, DurationUnit> UNITS =
|
private static final Map<Identifier, DurationUnit> UNITS =
|
||||||
Map.of(
|
Map.of(
|
||||||
Identifier.NS, DurationUnit.NANOS,
|
Identifier.NS, DurationUnit.NANOS,
|
||||||
|
|||||||
@@ -28,6 +28,8 @@ import org.pkl.core.util.EconomicMaps;
|
|||||||
public final class VmDynamic extends VmObject {
|
public final class VmDynamic extends VmObject {
|
||||||
private int cachedRegularMemberCount = -1;
|
private int cachedRegularMemberCount = -1;
|
||||||
|
|
||||||
|
private final int length;
|
||||||
|
|
||||||
private static final class EmptyHolder {
|
private static final class EmptyHolder {
|
||||||
private static final VmDynamic EMPTY =
|
private static final VmDynamic EMPTY =
|
||||||
new VmDynamic(
|
new VmDynamic(
|
||||||
@@ -37,8 +39,6 @@ public final class VmDynamic extends VmObject {
|
|||||||
0);
|
0);
|
||||||
}
|
}
|
||||||
|
|
||||||
private final int length;
|
|
||||||
|
|
||||||
public static VmDynamic empty() {
|
public static VmDynamic empty() {
|
||||||
return EmptyHolder.EMPTY;
|
return EmptyHolder.EMPTY;
|
||||||
}
|
}
|
||||||
@@ -48,7 +48,7 @@ public final class VmDynamic extends VmObject {
|
|||||||
VmObject parent,
|
VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
int length) {
|
int length) {
|
||||||
super(enclosingFrame, Objects.requireNonNull(parent), members);
|
super(PklShape.getRootShape(), enclosingFrame, Objects.requireNonNull(parent), members);
|
||||||
this.length = length;
|
this.length = length;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,8 +75,7 @@ public final class VmDynamic extends VmObject {
|
|||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public PObject export() {
|
public PObject export() {
|
||||||
var properties =
|
var properties = CollectionUtils.<String, Object>newLinkedHashMap(getCachedValueCount());
|
||||||
CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
|
|
||||||
|
|
||||||
iterateMemberValues(
|
iterateMemberValues(
|
||||||
(key, member, value) -> {
|
(key, member, value) -> {
|
||||||
@@ -109,7 +108,7 @@ public final class VmDynamic extends VmObject {
|
|||||||
other.force(false);
|
other.force(false);
|
||||||
if (getRegularMemberCount() != other.getRegularMemberCount()) return false;
|
if (getRegularMemberCount() != other.getRegularMemberCount()) return false;
|
||||||
|
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
Object key = cursor.getKey();
|
Object key = cursor.getKey();
|
||||||
if (isHiddenOrLocalProperty(key)) continue;
|
if (isHiddenOrLocalProperty(key)) continue;
|
||||||
@@ -130,7 +129,7 @@ public final class VmDynamic extends VmObject {
|
|||||||
|
|
||||||
force(false);
|
force(false);
|
||||||
var result = 0;
|
var result = 0;
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
|
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
var key = cursor.getKey();
|
var key = cursor.getKey();
|
||||||
@@ -150,8 +149,9 @@ public final class VmDynamic extends VmObject {
|
|||||||
if (cachedRegularMemberCount != -1) return cachedRegularMemberCount;
|
if (cachedRegularMemberCount != -1) return cachedRegularMemberCount;
|
||||||
|
|
||||||
var result = 0;
|
var result = 0;
|
||||||
for (var key : cachedValues.getKeys()) {
|
var cursor = getCachedValueEntries();
|
||||||
if (!isHiddenOrLocalProperty(key)) result += 1;
|
while (cursor.advance()) {
|
||||||
|
if (!isHiddenOrLocalProperty(cursor.getKey())) result += 1;
|
||||||
}
|
}
|
||||||
cachedRegularMemberCount = result;
|
cachedRegularMemberCount = result;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
@@ -23,9 +23,14 @@ import org.graalvm.collections.UnmodifiableEconomicMap;
|
|||||||
import org.pkl.core.ast.PklRootNode;
|
import org.pkl.core.ast.PklRootNode;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
|
import org.pkl.core.util.EmptyMapCursor;
|
||||||
|
import org.pkl.core.util.MapCursor;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class VmFunction extends VmObjectLike {
|
public final class VmFunction implements VmObjectLike {
|
||||||
|
// Own fields (VmFunction does not extend DynamicObject, so it has its own storage)
|
||||||
|
private final MaterializedFrame enclosingFrame;
|
||||||
|
private @Nullable Object extraStorage;
|
||||||
|
|
||||||
private final Object thisValue;
|
private final Object thisValue;
|
||||||
private final int paramCount;
|
private final int paramCount;
|
||||||
@@ -37,13 +42,28 @@ public final class VmFunction extends VmObjectLike {
|
|||||||
int paramCount,
|
int paramCount,
|
||||||
PklRootNode rootNode,
|
PklRootNode rootNode,
|
||||||
@Nullable Object extraStorage) {
|
@Nullable Object extraStorage) {
|
||||||
super(enclosingFrame);
|
this.enclosingFrame = enclosingFrame;
|
||||||
this.thisValue = thisValue;
|
this.thisValue = thisValue;
|
||||||
this.paramCount = paramCount;
|
this.paramCount = paramCount;
|
||||||
this.rootNode = rootNode;
|
this.rootNode = rootNode;
|
||||||
this.extraStorage = extraStorage;
|
this.extraStorage = extraStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MaterializedFrame getEnclosingFrame() {
|
||||||
|
return enclosingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public @Nullable Object getExtraStorage() {
|
||||||
|
return extraStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setExtraStorage(@Nullable Object extraStorage) {
|
||||||
|
this.extraStorage = extraStorage;
|
||||||
|
}
|
||||||
|
|
||||||
public RootCallTarget getCallTarget() {
|
public RootCallTarget getCallTarget() {
|
||||||
return rootNode.getCallTarget();
|
return rootNode.getCallTarget();
|
||||||
}
|
}
|
||||||
@@ -123,17 +143,27 @@ public final class VmFunction extends VmObjectLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean iterateMemberValues(MemberValueConsumer consumer) {
|
public MapCursor<Object, Object> getCachedValueEntries() {
|
||||||
|
return EmptyMapCursor.instance();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCachedValueCount() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean iterateMemberValues(VmObjectLike.MemberValueConsumer consumer) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean forceAndIterateMemberValues(ForcedMemberValueConsumer consumer) {
|
public boolean forceAndIterateMemberValues(VmObjectLike.ForcedMemberValueConsumer consumer) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean iterateAlreadyForcedMemberValues(ForcedMemberValueConsumer consumer) {
|
public boolean iterateAlreadyForcedMemberValues(VmObjectLike.ForcedMemberValueConsumer consumer) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -25,7 +25,7 @@ import org.pkl.core.util.Nullable;
|
|||||||
// Some code copied from kotlin.ranges.Progressions, kotlin.ranges.ProgressionIterators,
|
// Some code copied from kotlin.ranges.Progressions, kotlin.ranges.ProgressionIterators,
|
||||||
// kotlin.internal.ProgressionUtil (Apache 2).
|
// kotlin.internal.ProgressionUtil (Apache 2).
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmIntSeq extends VmValue implements Iterable<Long> {
|
public final class VmIntSeq implements VmValue, Iterable<Long> {
|
||||||
public final long start;
|
public final long start;
|
||||||
public final long end;
|
public final long end;
|
||||||
public final long step;
|
public final long step;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -87,7 +87,7 @@ public final class VmListing extends VmListingOrMapping {
|
|||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public List<Object> export() {
|
public List<Object> export() {
|
||||||
var properties = new ArrayList<>(EconomicMaps.size(cachedValues));
|
var properties = new ArrayList<>(getCachedValueCount());
|
||||||
|
|
||||||
iterateMemberValues(
|
iterateMemberValues(
|
||||||
(key, prop, value) -> {
|
(key, prop, value) -> {
|
||||||
@@ -121,7 +121,7 @@ public final class VmListing extends VmListingOrMapping {
|
|||||||
force(false);
|
force(false);
|
||||||
other.force(false);
|
other.force(false);
|
||||||
|
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
var key = cursor.getKey();
|
var key = cursor.getKey();
|
||||||
if (key instanceof Identifier) continue;
|
if (key instanceof Identifier) continue;
|
||||||
@@ -142,7 +142,7 @@ public final class VmListing extends VmListingOrMapping {
|
|||||||
|
|
||||||
force(false);
|
force(false);
|
||||||
var result = 0;
|
var result = 0;
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
|
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
var key = cursor.getKey();
|
var key = cursor.getKey();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,7 +23,6 @@ import org.graalvm.collections.UnmodifiableEconomicMap;
|
|||||||
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
import org.pkl.core.ast.member.ListingOrMappingTypeCastNode;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
import org.pkl.core.ast.type.TypeNode;
|
import org.pkl.core.ast.type.TypeNode;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public abstract class VmListingOrMapping extends VmObject {
|
public abstract class VmListingOrMapping extends VmObject {
|
||||||
@@ -32,24 +31,24 @@ public abstract class VmListingOrMapping extends VmObject {
|
|||||||
private final @Nullable Object typeCheckReceiver;
|
private final @Nullable Object typeCheckReceiver;
|
||||||
private final @Nullable VmObjectLike typeCheckOwner;
|
private final @Nullable VmObjectLike typeCheckOwner;
|
||||||
|
|
||||||
public VmListingOrMapping(
|
protected VmListingOrMapping(
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
@Nullable VmObject parent,
|
@Nullable VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
super(enclosingFrame, parent, members);
|
super(PklShape.getRootShape(), enclosingFrame, parent, members);
|
||||||
typeCastNode = null;
|
typeCastNode = null;
|
||||||
typeCheckReceiver = null;
|
typeCheckReceiver = null;
|
||||||
typeCheckOwner = null;
|
typeCheckOwner = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VmListingOrMapping(
|
protected VmListingOrMapping(
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
@Nullable VmObject parent,
|
@Nullable VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
ListingOrMappingTypeCastNode typeCastNode,
|
ListingOrMappingTypeCastNode typeCastNode,
|
||||||
Object typeCheckReceiver,
|
Object typeCheckReceiver,
|
||||||
VmObjectLike typeCheckOwner) {
|
VmObjectLike typeCheckOwner) {
|
||||||
super(enclosingFrame, parent, members);
|
super(PklShape.getRootShape(), enclosingFrame, parent, members);
|
||||||
this.typeCastNode = typeCastNode;
|
this.typeCastNode = typeCastNode;
|
||||||
this.typeCheckReceiver = typeCheckReceiver;
|
this.typeCheckReceiver = typeCheckReceiver;
|
||||||
this.typeCheckOwner = typeCheckOwner;
|
this.typeCheckOwner = typeCheckOwner;
|
||||||
@@ -88,7 +87,7 @@ public abstract class VmListingOrMapping extends VmObject {
|
|||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public final @Nullable Object getCachedValue(Object key) {
|
public final @Nullable Object getCachedValue(Object key) {
|
||||||
var result = EconomicMaps.get(cachedValues, key);
|
var result = super.getCachedValue(key);
|
||||||
// if this object has members, `this[key]` may differ from `parent[key]`, so stop the search
|
// if this object has members, `this[key]` may differ from `parent[key]`, so stop the search
|
||||||
if (result != null || !members.isEmpty()) return result;
|
if (result != null || !members.isEmpty()) return result;
|
||||||
|
|
||||||
|
|||||||
@@ -30,7 +30,7 @@ import org.pkl.core.util.paguro.RrbTree;
|
|||||||
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
|
import org.pkl.core.util.paguro.RrbTree.ImRrbt;
|
||||||
import org.pkl.core.util.paguro.RrbTree.MutRrbt;
|
import org.pkl.core.util.paguro.RrbTree.MutRrbt;
|
||||||
|
|
||||||
public final class VmMap extends VmValue implements Iterable<Map.Entry<Object, Object>> {
|
public final class VmMap implements VmValue, Iterable<Map.Entry<Object, Object>> {
|
||||||
public static final VmMap EMPTY = new VmMap(PersistentHashMap.empty(), RrbTree.empty());
|
public static final VmMap EMPTY = new VmMap(PersistentHashMap.empty(), RrbTree.empty());
|
||||||
|
|
||||||
private final ImMap<Object, Object> map;
|
private final ImMap<Object, Object> map;
|
||||||
|
|||||||
@@ -93,7 +93,7 @@ public final class VmMapping extends VmListingOrMapping {
|
|||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public Map<Object, Object> export() {
|
public Map<Object, Object> export() {
|
||||||
var properties = CollectionUtils.newLinkedHashMap(EconomicMaps.size(cachedValues));
|
var properties = CollectionUtils.newLinkedHashMap(getCachedValueCount());
|
||||||
|
|
||||||
iterateMemberValues(
|
iterateMemberValues(
|
||||||
(key, prop, value) -> {
|
(key, prop, value) -> {
|
||||||
@@ -128,7 +128,7 @@ public final class VmMapping extends VmListingOrMapping {
|
|||||||
other.force(false);
|
other.force(false);
|
||||||
if (getLength() != other.getLength()) return false;
|
if (getLength() != other.getLength()) return false;
|
||||||
|
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
Object key = cursor.getKey();
|
Object key = cursor.getKey();
|
||||||
if (key instanceof Identifier) continue;
|
if (key instanceof Identifier) continue;
|
||||||
@@ -149,7 +149,7 @@ public final class VmMapping extends VmListingOrMapping {
|
|||||||
|
|
||||||
force(false);
|
force(false);
|
||||||
var result = 0;
|
var result = 0;
|
||||||
var cursor = cachedValues.getEntries();
|
var cursor = getCachedValueEntries();
|
||||||
|
|
||||||
while (cursor.advance()) {
|
while (cursor.advance()) {
|
||||||
var key = cursor.getKey();
|
var key = cursor.getKey();
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -20,7 +20,7 @@ import org.pkl.core.PNull;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmNull extends VmValue {
|
public final class VmNull implements VmValue {
|
||||||
private static final VmNull WITHOUT_DEFAULT = new VmNull(null);
|
private static final VmNull WITHOUT_DEFAULT = new VmNull(null);
|
||||||
|
|
||||||
// worthwhile to create this lazily?
|
// worthwhile to create this lazily?
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -18,42 +18,69 @@ package org.pkl.core.runtime;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObject;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObjectLibrary;
|
||||||
|
import com.oracle.truffle.api.object.Shape;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import org.graalvm.collections.EconomicMap;
|
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
import org.pkl.core.util.CollectionUtils;
|
import org.pkl.core.util.CollectionUtils;
|
||||||
|
import org.pkl.core.util.DynamicObjectMapCursor;
|
||||||
import org.pkl.core.util.EconomicMaps;
|
import org.pkl.core.util.EconomicMaps;
|
||||||
|
import org.pkl.core.util.MapCursor;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
/** Corresponds to `pkl.base#Object`. */
|
/**
|
||||||
public abstract class VmObject extends VmObjectLike {
|
* Corresponds to `pkl.base#Object`.
|
||||||
|
*
|
||||||
|
* <p>Extends {@link DynamicObject} to leverage Truffle's object storage and inline caching
|
||||||
|
* capabilities. Cached property values are stored directly in this object using the Dynamic Object
|
||||||
|
* Model.
|
||||||
|
*/
|
||||||
|
public abstract class VmObject extends DynamicObject implements VmObjectLike {
|
||||||
|
// moved from VmObjectLike
|
||||||
|
protected final MaterializedFrame enclosingFrame;
|
||||||
|
protected @Nullable Object extraStorage;
|
||||||
|
|
||||||
@CompilationFinal protected @Nullable VmObject parent;
|
@CompilationFinal protected @Nullable VmObject parent;
|
||||||
protected final UnmodifiableEconomicMap<Object, ObjectMember> members;
|
protected final UnmodifiableEconomicMap<Object, ObjectMember> members;
|
||||||
protected final EconomicMap<Object, Object> cachedValues;
|
|
||||||
|
|
||||||
protected int cachedHash;
|
protected int cachedHash;
|
||||||
private boolean forced;
|
private boolean forced;
|
||||||
|
|
||||||
public VmObject(
|
/**
|
||||||
MaterializedFrame enclosingFrame,
|
* Separate cache for local property values.
|
||||||
@Nullable VmObject parent,
|
*
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
* <p>This is kept separate from the DynamicObject storage to avoid shape transitions.
|
||||||
EconomicMap<Object, Object> cachedValues) {
|
*/
|
||||||
super(enclosingFrame);
|
private @Nullable IdentityHashMap<ObjectMember, Object> localPropertyCache;
|
||||||
this.parent = parent;
|
|
||||||
this.members = members;
|
|
||||||
this.cachedValues = cachedValues;
|
|
||||||
|
|
||||||
assert parent != this;
|
protected VmObject(
|
||||||
}
|
Shape shape,
|
||||||
|
|
||||||
public VmObject(
|
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
@Nullable VmObject parent,
|
@Nullable VmObject parent,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
this(enclosingFrame, parent, members, EconomicMaps.create());
|
super(shape);
|
||||||
|
this.enclosingFrame = enclosingFrame;
|
||||||
|
this.parent = parent;
|
||||||
|
this.members = members;
|
||||||
|
assert parent != this;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final MaterializedFrame getEnclosingFrame() {
|
||||||
|
return enclosingFrame;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final @Nullable Object getExtraStorage() {
|
||||||
|
return extraStorage;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public final void setExtraStorage(@Nullable Object extraStorage) {
|
||||||
|
this.extraStorage = extraStorage;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void lateInitParent(VmObject parent) {
|
public final void lateInitParent(VmObject parent) {
|
||||||
@@ -67,11 +94,13 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
public final boolean hasMember(Object key) {
|
public final boolean hasMember(Object key) {
|
||||||
return EconomicMaps.containsKey(members, key);
|
return EconomicMaps.containsKey(members, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
public final @Nullable ObjectMember getMember(Object key) {
|
public final @Nullable ObjectMember getMember(Object key) {
|
||||||
return EconomicMaps.get(members, key);
|
return EconomicMaps.get(members, key);
|
||||||
}
|
}
|
||||||
@@ -82,23 +111,81 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
public @Nullable Object getCachedValue(Object key) {
|
public @Nullable Object getCachedValue(Object key) {
|
||||||
return EconomicMaps.get(cachedValues, key);
|
return DynamicObjectLibrary.getUncached().getOrDefault(this, key, null);
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final void setCachedValue(Object key, Object value) {
|
|
||||||
EconomicMaps.put(cachedValues, key, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public final boolean hasCachedValue(Object key) {
|
|
||||||
return EconomicMaps.containsKey(cachedValues, key);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public final boolean iterateMemberValues(MemberValueConsumer consumer) {
|
public void setCachedValue(Object key, Object value) {
|
||||||
|
DynamicObjectLibrary.getUncached().put(this, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
|
public boolean hasCachedValue(Object key) {
|
||||||
|
return DynamicObjectLibrary.getUncached().containsKey(this, key);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public MapCursor<Object, Object> getCachedValueEntries() {
|
||||||
|
return new DynamicObjectMapCursor(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
|
public int getCachedValueCount() {
|
||||||
|
return DynamicObjectLibrary.getUncached().getKeyArray(this).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Clean all cached values. Local or otherwise. Resets cached values to null without removing the
|
||||||
|
* keys, preserving the object's shape for pre-allocated slots.
|
||||||
|
*/
|
||||||
|
@TruffleBoundary
|
||||||
|
public void cleanAllCachedValues() {
|
||||||
|
if (localPropertyCache != null) {
|
||||||
|
localPropertyCache.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
var lib = DynamicObjectLibrary.getUncached();
|
||||||
|
Object[] keys = lib.getKeyArray(this);
|
||||||
|
for (Object key : keys) {
|
||||||
|
lib.put(this, key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
forced = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a cached local property value.
|
||||||
|
*
|
||||||
|
* @param property the ObjectMember representing the local property declaration
|
||||||
|
* @return the cached value, or null if not cached
|
||||||
|
*/
|
||||||
|
@TruffleBoundary
|
||||||
|
public @Nullable Object getLocalCachedValue(ObjectMember property) {
|
||||||
|
return localPropertyCache == null ? null : localPropertyCache.get(property);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a cached local property value.
|
||||||
|
*
|
||||||
|
* @param property the ObjectMember representing the local property declaration
|
||||||
|
* @param value the value to cache
|
||||||
|
*/
|
||||||
|
@TruffleBoundary
|
||||||
|
public void setLocalCachedValue(ObjectMember property, Object value) {
|
||||||
|
if (localPropertyCache == null) {
|
||||||
|
localPropertyCache = new IdentityHashMap<>(4);
|
||||||
|
}
|
||||||
|
localPropertyCache.put(property, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
@TruffleBoundary
|
||||||
|
public final boolean iterateMemberValues(VmObjectLike.MemberValueConsumer consumer) {
|
||||||
var visited = new HashSet<>();
|
var visited = new HashSet<>();
|
||||||
return iterateMembers(
|
return iterateMembers(
|
||||||
(key, member) -> {
|
(key, member) -> {
|
||||||
@@ -112,14 +199,16 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public final boolean forceAndIterateMemberValues(ForcedMemberValueConsumer consumer) {
|
public final boolean forceAndIterateMemberValues(
|
||||||
|
VmObjectLike.ForcedMemberValueConsumer consumer) {
|
||||||
force(false, false);
|
force(false, false);
|
||||||
return iterateAlreadyForcedMemberValues(consumer);
|
return iterateAlreadyForcedMemberValues(consumer);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
public final boolean iterateAlreadyForcedMemberValues(ForcedMemberValueConsumer consumer) {
|
public final boolean iterateAlreadyForcedMemberValues(
|
||||||
|
VmObjectLike.ForcedMemberValueConsumer consumer) {
|
||||||
var visited = new HashSet<>();
|
var visited = new HashSet<>();
|
||||||
return iterateMembers(
|
return iterateMembers(
|
||||||
(key, member) -> {
|
(key, member) -> {
|
||||||
@@ -158,6 +247,9 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
|
|
||||||
if (recurse) forced = true;
|
if (recurse) forced = true;
|
||||||
|
|
||||||
|
// use cached call node from this object's class to avoid getUncached() overhead
|
||||||
|
var callNode = getVmClass().getCachedCallNode();
|
||||||
|
|
||||||
try {
|
try {
|
||||||
for (VmObjectLike owner = this; owner != null; owner = owner.getParent()) {
|
for (VmObjectLike owner = this; owner != null; owner = owner.getParent()) {
|
||||||
var cursor = EconomicMaps.getEntries(owner.getMembers());
|
var cursor = EconomicMaps.getEntries(owner.getMembers());
|
||||||
@@ -174,7 +266,7 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
var memberValue = getCachedValue(memberKey);
|
var memberValue = getCachedValue(memberKey);
|
||||||
if (memberValue == null) {
|
if (memberValue == null) {
|
||||||
try {
|
try {
|
||||||
memberValue = VmUtils.doReadMember(this, owner, memberKey, member);
|
memberValue = VmUtils.doReadMember(this, owner, memberKey, member, true, callNode);
|
||||||
} catch (VmUndefinedValueException e) {
|
} catch (VmUndefinedValueException e) {
|
||||||
if (!allowUndefinedValues) throw e;
|
if (!allowUndefinedValues) throw e;
|
||||||
continue;
|
continue;
|
||||||
@@ -208,7 +300,7 @@ public abstract class VmObject extends VmObjectLike {
|
|||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected final Map<String, Object> exportMembers() {
|
protected final Map<String, Object> exportMembers() {
|
||||||
var result = CollectionUtils.<String, Object>newLinkedHashMap(EconomicMaps.size(cachedValues));
|
var result = CollectionUtils.<String, Object>newLinkedHashMap(getCachedValueCount());
|
||||||
|
|
||||||
iterateMemberValues(
|
iterateMemberValues(
|
||||||
(key, member, value) -> {
|
(key, member, value) -> {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -15,53 +15,39 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.core.runtime;
|
package org.pkl.core.runtime;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|
||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
import java.util.function.BiFunction;
|
import java.util.function.BiFunction;
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
import org.pkl.core.ast.member.ObjectMember;
|
import org.pkl.core.ast.member.ObjectMember;
|
||||||
|
import org.pkl.core.util.MapCursor;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Corresponds to `pkl.base#Object|pkl.base#Function`. The lexical scope is a chain of
|
* Corresponds to `pkl.base#Object|pkl.base#Function`. The lexical scope is a chain of
|
||||||
* `VmObjectLike` instances.
|
* `VmObjectLike` instances.
|
||||||
*/
|
*/
|
||||||
public abstract class VmObjectLike extends VmValue {
|
public interface VmObjectLike extends VmValue {
|
||||||
/** The frame that was active when this object was instantiated. * */
|
/** The frame that was active when this object was instantiated. * */
|
||||||
protected final MaterializedFrame enclosingFrame;
|
MaterializedFrame getEnclosingFrame();
|
||||||
|
|
||||||
protected @Nullable Object extraStorage;
|
@Nullable
|
||||||
|
Object getExtraStorage();
|
||||||
|
|
||||||
protected VmObjectLike(MaterializedFrame enclosingFrame) {
|
void setExtraStorage(@Nullable Object extraStorage);
|
||||||
this.enclosingFrame = enclosingFrame;
|
|
||||||
|
default boolean hasExtraStorage() {
|
||||||
|
return getExtraStorage() != null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final MaterializedFrame getEnclosingFrame() {
|
default @Nullable Object getEnclosingReceiver() {
|
||||||
return enclosingFrame;
|
return VmUtils.getReceiverOrNull(getEnclosingFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final @Nullable Object getEnclosingReceiver() {
|
default @Nullable VmObjectLike getEnclosingOwner() {
|
||||||
return VmUtils.getReceiverOrNull(enclosingFrame);
|
return VmUtils.getOwnerOrNull(getEnclosingFrame());
|
||||||
}
|
}
|
||||||
|
|
||||||
public final @Nullable VmObjectLike getEnclosingOwner() {
|
default boolean isModuleObject() {
|
||||||
return VmUtils.getOwnerOrNull(enclosingFrame);
|
|
||||||
}
|
|
||||||
|
|
||||||
public final boolean hasExtraStorage() {
|
|
||||||
return extraStorage != null;
|
|
||||||
}
|
|
||||||
|
|
||||||
public Object getExtraStorage() {
|
|
||||||
assert extraStorage != null;
|
|
||||||
return extraStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public final void setExtraStorage(@Nullable Object extraStorage) {
|
|
||||||
this.extraStorage = extraStorage;
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean isModuleObject() {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,41 +55,45 @@ public abstract class VmObjectLike extends VmValue {
|
|||||||
* Returns the parent object in the prototype chain. For each concrete subclass X of VmObjectLike,
|
* Returns the parent object in the prototype chain. For each concrete subclass X of VmObjectLike,
|
||||||
* the exact return type of this method is `X|VmTyped`.
|
* the exact return type of this method is `X|VmTyped`.
|
||||||
*/
|
*/
|
||||||
public abstract @Nullable VmObjectLike getParent();
|
@Nullable
|
||||||
|
VmObjectLike getParent();
|
||||||
|
|
||||||
/** Always prefer this method over `getMembers().containsKey(key)`. */
|
/** Always prefer this method over `getMembers().containsKey(key)`. */
|
||||||
@TruffleBoundary
|
boolean hasMember(Object key);
|
||||||
public abstract boolean hasMember(Object key);
|
|
||||||
|
|
||||||
/** Always prefer this method over `getMembers().get(key)`. */
|
/** Always prefer this method over `getMembers().get(key)`. */
|
||||||
@TruffleBoundary
|
@Nullable
|
||||||
public abstract @Nullable ObjectMember getMember(Object key);
|
ObjectMember getMember(Object key);
|
||||||
|
|
||||||
/** Returns the declared members of this object. */
|
/** Returns the declared members of this object. */
|
||||||
public abstract UnmodifiableEconomicMap<Object, ObjectMember> getMembers();
|
UnmodifiableEconomicMap<Object, ObjectMember> getMembers();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Reads from the properties cache for this object. The cache contains the values of all members
|
* Reads from the properties cache for this object. The cache contains the values of all members
|
||||||
* defined in this object or an ancestor thereof which have been requested with this object as the
|
* defined in this object or an ancestor thereof which have been requested with this object as the
|
||||||
* receiver.
|
* receiver.
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
@Nullable
|
||||||
public abstract @Nullable Object getCachedValue(Object key);
|
Object getCachedValue(Object key);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Writes to the properties cache for this object. The cache contains the values of all members
|
* Writes to the properties cache for this object. The cache contains the values of all members
|
||||||
* defined in this object or an ancestor thereof which have been requested with this object as the
|
* defined in this object or an ancestor thereof which have been requested with this object as the
|
||||||
* receiver.
|
* receiver.
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
void setCachedValue(Object key, Object value);
|
||||||
public abstract void setCachedValue(Object key, Object value);
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no
|
* Prefer this method over {@link #getCachedValue} if the value is not required. (There is no
|
||||||
* point in calling this method to determine whether to call {@link #getCachedValue}.)
|
* point in calling this method to determine whether to call {@link #getCachedValue}.)
|
||||||
*/
|
*/
|
||||||
@TruffleBoundary
|
boolean hasCachedValue(Object key);
|
||||||
public abstract boolean hasCachedValue(Object key);
|
|
||||||
|
/** Returns a cursor for iterating over all cached values in this object. */
|
||||||
|
MapCursor<Object, Object> getCachedValueEntries();
|
||||||
|
|
||||||
|
/** Returns the number of cached values in this object. */
|
||||||
|
int getCachedValueCount();
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over member definitions and their values in order of their definition, from the top of
|
* Iterates over member definitions and their values in order of their definition, from the top of
|
||||||
@@ -118,15 +108,15 @@ public abstract class VmObjectLike extends VmValue {
|
|||||||
* remaining members are not visited, and `false` is returned. Otherwise, all members are visited,
|
* remaining members are not visited, and `false` is returned. Otherwise, all members are visited,
|
||||||
* and `true` is returned.
|
* and `true` is returned.
|
||||||
*/
|
*/
|
||||||
public abstract boolean iterateMemberValues(MemberValueConsumer consumer);
|
boolean iterateMemberValues(MemberValueConsumer consumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Same as {@link #iterateMemberValues} except that it first performs a shallow {@link #force}. As
|
* Same as {@link #iterateMemberValues} except that it first performs a shallow {@link #force}. As
|
||||||
* a consequence, values passed to {@code consumer} are guaranteed to be non-null.
|
* a consequence, values passed to {@code consumer} are guaranteed to be non-null.
|
||||||
*/
|
*/
|
||||||
public abstract boolean forceAndIterateMemberValues(ForcedMemberValueConsumer consumer);
|
boolean forceAndIterateMemberValues(ForcedMemberValueConsumer consumer);
|
||||||
|
|
||||||
public abstract boolean iterateAlreadyForcedMemberValues(ForcedMemberValueConsumer consumer);
|
boolean iterateAlreadyForcedMemberValues(ForcedMemberValueConsumer consumer);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Iterates over member definitions in order of their definition, from the top of the prototype
|
* Iterates over member definitions in order of their definition, from the top of the prototype
|
||||||
@@ -135,19 +125,20 @@ public abstract class VmObjectLike extends VmValue {
|
|||||||
* members are not visited, and `false` is returned. Otherwise, all members are visited, and
|
* members are not visited, and `false` is returned. Otherwise, all members are visited, and
|
||||||
* `true` is returned.
|
* `true` is returned.
|
||||||
*/
|
*/
|
||||||
public abstract boolean iterateMembers(BiFunction<Object, ObjectMember, Boolean> consumer);
|
boolean iterateMembers(BiFunction<Object, ObjectMember, Boolean> consumer);
|
||||||
|
|
||||||
/** Forces shallow or recursive (deep) evaluation of this object. */
|
/** Forces shallow or recursive (deep) evaluation of this object. */
|
||||||
public abstract void force(boolean allowUndefinedValues, boolean recurse);
|
void force(boolean allowUndefinedValues, boolean recurse);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Exports this object to an external representation. Does not export local, hidden, or external
|
* Exports this object to an external representation. Does not export local, hidden, or external
|
||||||
* properties
|
* properties
|
||||||
*/
|
*/
|
||||||
public abstract Object export();
|
@Override
|
||||||
|
Object export();
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface MemberValueConsumer {
|
interface MemberValueConsumer {
|
||||||
/**
|
/**
|
||||||
* Returns true if {@link #iterateMemberValues} should continue calling this method for the
|
* Returns true if {@link #iterateMemberValues} should continue calling this method for the
|
||||||
* remaining members, and false otherwise.
|
* remaining members, and false otherwise.
|
||||||
@@ -156,7 +147,7 @@ public abstract class VmObjectLike extends VmValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@FunctionalInterface
|
@FunctionalInterface
|
||||||
public interface ForcedMemberValueConsumer {
|
interface ForcedMemberValueConsumer {
|
||||||
/**
|
/**
|
||||||
* Returns true if {@link #forceAndIterateMemberValues} should continue calling this method for
|
* Returns true if {@link #forceAndIterateMemberValues} should continue calling this method for
|
||||||
* the remaining members, and false otherwise.
|
* the remaining members, and false otherwise.
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,7 +23,7 @@ import org.pkl.core.Pair;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmPair extends VmValue implements Iterable<Object> {
|
public final class VmPair implements VmValue, Iterable<Object> {
|
||||||
private final Object first;
|
private final Object first;
|
||||||
private final Object second;
|
private final Object second;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -23,7 +23,7 @@ import org.pkl.core.ValueFormatter;
|
|||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
@ValueType
|
@ValueType
|
||||||
public final class VmRegex extends VmValue {
|
public final class VmRegex implements VmValue {
|
||||||
private final Pattern pattern;
|
private final Pattern pattern;
|
||||||
|
|
||||||
public VmRegex(Pattern pattern) {
|
public VmRegex(Pattern pattern) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -36,7 +36,7 @@ import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
|
|||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public final class VmTypeAlias extends VmValue {
|
public final class VmTypeAlias implements VmValue {
|
||||||
private final SourceSection sourceSection;
|
private final SourceSection sourceSection;
|
||||||
private final SourceSection headerSection;
|
private final SourceSection headerSection;
|
||||||
private final SourceSection @Nullable [] docComment;
|
private final SourceSection @Nullable [] docComment;
|
||||||
|
|||||||
@@ -19,6 +19,9 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
|||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||||
import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode;
|
import com.oracle.truffle.api.instrumentation.InstrumentableNode.WrapperNode;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObject;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObjectLibrary;
|
||||||
|
import com.oracle.truffle.api.object.Shape;
|
||||||
import org.graalvm.collections.EconomicMap;
|
import org.graalvm.collections.EconomicMap;
|
||||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||||
import org.pkl.core.Composite;
|
import org.pkl.core.Composite;
|
||||||
@@ -34,14 +37,99 @@ import org.pkl.core.util.Nullable;
|
|||||||
public final class VmTyped extends VmObject {
|
public final class VmTyped extends VmObject {
|
||||||
@CompilationFinal @LateInit private VmClass clazz;
|
@CompilationFinal @LateInit private VmClass clazz;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new VmTyped with the class's instance shape.
|
||||||
|
*
|
||||||
|
* @param enclosingFrame the frame that was active when this object was instantiated
|
||||||
|
* @param parent the parent in the prototype chain, or null
|
||||||
|
* @param clazz the class of this object, or null if it will be late-initialized
|
||||||
|
* @param members the declared members of this object
|
||||||
|
*/
|
||||||
public VmTyped(
|
public VmTyped(
|
||||||
MaterializedFrame enclosingFrame,
|
MaterializedFrame enclosingFrame,
|
||||||
@Nullable VmTyped parent,
|
@Nullable VmTyped parent,
|
||||||
// null -> will be initialized using lateInitVmClass() later
|
// null -> will be initialized using lateInitVmClass() later
|
||||||
@Nullable VmClass clazz,
|
@Nullable VmClass clazz,
|
||||||
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
super(enclosingFrame, parent, members);
|
this(enclosingFrame, parent, clazz, members, getShapeForClass(clazz));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new VmTyped with a custom shape.
|
||||||
|
*
|
||||||
|
* <p>This constructor is used when a specific shape is needed, such as when amending an object
|
||||||
|
* where the shape may differ from the class's base instance shape.
|
||||||
|
*
|
||||||
|
* @param enclosingFrame the frame that was active when this object was instantiated
|
||||||
|
* @param parent the parent in the prototype chain, or null
|
||||||
|
* @param clazz the class of this object, or null if it will be late-initialized
|
||||||
|
* @param members the declared members of this object
|
||||||
|
* @param shape the Truffle shape for this object's cached value storage
|
||||||
|
*/
|
||||||
|
public VmTyped(
|
||||||
|
MaterializedFrame enclosingFrame,
|
||||||
|
@Nullable VmTyped parent,
|
||||||
|
@Nullable VmClass clazz,
|
||||||
|
UnmodifiableEconomicMap<Object, ObjectMember> members,
|
||||||
|
Shape shape) {
|
||||||
|
super(shape, enclosingFrame, parent, members);
|
||||||
this.clazz = clazz;
|
this.clazz = clazz;
|
||||||
|
// pre-allocate cache slots for all members to stabilize the shape
|
||||||
|
preallocateCacheSlots(members);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Pre-allocates cache slots for all members by putting null values. This creates shape
|
||||||
|
* transitions upfront so all instances share the same final shape.
|
||||||
|
*/
|
||||||
|
private void preallocateCacheSlots(UnmodifiableEconomicMap<Object, ObjectMember> members) {
|
||||||
|
var library = DynamicObjectLibrary.getUncached();
|
||||||
|
var cursor = members.getEntries();
|
||||||
|
while (cursor.advance()) {
|
||||||
|
library.put(this, cursor.getKey(), null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Shape getShapeForClass(@Nullable VmClass clazz) {
|
||||||
|
return clazz != null ? clazz.getInstanceShape() : PklShape.getRootShape();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Returns this object for cached value storage. */
|
||||||
|
public DynamicObject getCachedValuesStorage() {
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets a cached value using the provided library for PE-optimized access.
|
||||||
|
*
|
||||||
|
* @param key the property key
|
||||||
|
* @param library the DynamicObjectLibrary to use (should be cached via @CachedLibrary)
|
||||||
|
* @return the cached value, or null if not present
|
||||||
|
*/
|
||||||
|
public @Nullable Object getCachedValue(Object key, DynamicObjectLibrary library) {
|
||||||
|
return library.getOrDefault(this, key, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets a cached value using the provided library for PE-optimized access.
|
||||||
|
*
|
||||||
|
* @param key the property key
|
||||||
|
* @param value the value to cache
|
||||||
|
* @param library the DynamicObjectLibrary to use (should be cached via @CachedLibrary)
|
||||||
|
*/
|
||||||
|
public void setCachedValue(Object key, Object value, DynamicObjectLibrary library) {
|
||||||
|
library.put(this, key, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks if a cached value exists using the provided library for PE-optimized access.
|
||||||
|
*
|
||||||
|
* @param key the property key
|
||||||
|
* @param library the DynamicObjectLibrary to use (should be cached via @CachedLibrary)
|
||||||
|
* @return true if a value is cached for this key
|
||||||
|
*/
|
||||||
|
public boolean hasCachedValue(Object key, DynamicObjectLibrary library) {
|
||||||
|
return library.containsKey(this, key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void lateInitVmClass(VmClass clazz) {
|
public void lateInitVmClass(VmClass clazz) {
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
/*
|
/*
|
||||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
*
|
*
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
* you may not use this file except in compliance with the License.
|
* you may not use this file except in compliance with the License.
|
||||||
@@ -17,30 +17,30 @@ package org.pkl.core.runtime;
|
|||||||
|
|
||||||
import org.pkl.core.util.Nullable;
|
import org.pkl.core.util.Nullable;
|
||||||
|
|
||||||
public abstract class VmValue {
|
public interface VmValue {
|
||||||
public abstract VmClass getVmClass();
|
VmClass getVmClass();
|
||||||
|
|
||||||
public VmTyped getPrototype() {
|
default VmTyped getPrototype() {
|
||||||
return getVmClass().getPrototype();
|
return getVmClass().getPrototype();
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isPrototype() {
|
default boolean isPrototype() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isDynamic() {
|
default boolean isDynamic() {
|
||||||
return this instanceof VmDynamic;
|
return this instanceof VmDynamic;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isListing() {
|
default boolean isListing() {
|
||||||
return this instanceof VmListing;
|
return this instanceof VmListing;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isMapping() {
|
default boolean isMapping() {
|
||||||
return this instanceof VmMapping;
|
return this instanceof VmMapping;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isTyped() {
|
default boolean isTyped() {
|
||||||
return this instanceof VmTyped;
|
return this instanceof VmTyped;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -48,21 +48,21 @@ public abstract class VmValue {
|
|||||||
* Tells if this value is a {@link VmCollection}, {@link VmListing}, or {@link VmDynamic} with
|
* Tells if this value is a {@link VmCollection}, {@link VmListing}, or {@link VmDynamic} with
|
||||||
* {@link VmDynamic#hasElements() elements}.
|
* {@link VmDynamic#hasElements() elements}.
|
||||||
*/
|
*/
|
||||||
public boolean isSequence() {
|
default boolean isSequence() {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Forces recursive (deep) evaluation of this value. */
|
/** Forces recursive (deep) evaluation of this value. */
|
||||||
public abstract void force(boolean allowUndefinedValues);
|
void force(boolean allowUndefinedValues);
|
||||||
|
|
||||||
public abstract Object export();
|
Object export();
|
||||||
|
|
||||||
public abstract void accept(VmValueVisitor visitor);
|
void accept(VmValueVisitor visitor);
|
||||||
|
|
||||||
public abstract <T> T accept(VmValueConverter<T> converter, Iterable<Object> path);
|
<T> T accept(VmValueConverter<T> converter, Iterable<Object> path);
|
||||||
|
|
||||||
/** Forces recursive (deep) evaluation of the given value. */
|
/** Forces recursive (deep) evaluation of the given value. */
|
||||||
public static void force(Object value, boolean allowUndefinedValues) {
|
static void force(Object value, boolean allowUndefinedValues) {
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
vmValue.force(allowUndefinedValues);
|
vmValue.force(allowUndefinedValues);
|
||||||
}
|
}
|
||||||
@@ -72,7 +72,7 @@ public abstract class VmValue {
|
|||||||
* Used to export values other than object member values. Such values aren't `@Nullable` (but can
|
* Used to export values other than object member values. Such values aren't `@Nullable` (but can
|
||||||
* be `VmNull`).
|
* be `VmNull`).
|
||||||
*/
|
*/
|
||||||
public static Object export(Object value) {
|
static Object export(Object value) {
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
return vmValue.export();
|
return vmValue.export();
|
||||||
}
|
}
|
||||||
@@ -80,14 +80,10 @@ public abstract class VmValue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** Used to export object member values. Such values are `null` if they haven't been forced. */
|
/** Used to export object member values. Such values are `null` if they haven't been forced. */
|
||||||
public static @Nullable Object exportNullable(@Nullable Object value) {
|
static @Nullable Object exportNullable(@Nullable Object value) {
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
return vmValue.export();
|
return vmValue.export();
|
||||||
}
|
}
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Enables calling `vmValue.equals()` when not behind a Truffle boundary. */
|
|
||||||
@Override
|
|
||||||
public abstract boolean equals(Object obj);
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,65 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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 org.pkl.core.util;
|
||||||
|
|
||||||
|
import com.oracle.truffle.api.object.DynamicObject;
|
||||||
|
import com.oracle.truffle.api.object.DynamicObjectLibrary;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A {@link MapCursor} implementation that iterates over the properties of a {@link DynamicObject}.
|
||||||
|
*
|
||||||
|
* <p>This cursor provides allocation-free iteration over DynamicObject properties by caching the
|
||||||
|
* key array at construction time and accessing values on demand.
|
||||||
|
*/
|
||||||
|
public final class DynamicObjectMapCursor implements MapCursor<Object, Object> {
|
||||||
|
private final DynamicObject object;
|
||||||
|
private final DynamicObjectLibrary library;
|
||||||
|
private final Object[] keys;
|
||||||
|
private int index = -1;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a cursor for iterating over the given DynamicObject's properties.
|
||||||
|
*
|
||||||
|
* @param object the DynamicObject to iterate over
|
||||||
|
*/
|
||||||
|
public DynamicObjectMapCursor(DynamicObject object) {
|
||||||
|
this.object = object;
|
||||||
|
this.library = DynamicObjectLibrary.getUncached();
|
||||||
|
this.keys = library.getKeyArray(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advance() {
|
||||||
|
index++;
|
||||||
|
return index < keys.length;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getKey() {
|
||||||
|
if (index < 0 || index >= keys.length) {
|
||||||
|
throw new IllegalStateException("Cursor not positioned on a valid entry");
|
||||||
|
}
|
||||||
|
return keys[index];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getValue() {
|
||||||
|
if (index < 0 || index >= keys.length) {
|
||||||
|
throw new IllegalStateException("Cursor not positioned on a valid entry");
|
||||||
|
}
|
||||||
|
return library.getOrDefault(object, keys[index], null);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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 org.pkl.core.util;
|
||||||
|
|
||||||
|
/** A cursor that iterates over zero entries. */
|
||||||
|
public final class EmptyMapCursor<K, V> implements MapCursor<K, V> {
|
||||||
|
private static final EmptyMapCursor<Object, Object> INSTANCE = new EmptyMapCursor<>();
|
||||||
|
|
||||||
|
private EmptyMapCursor() {}
|
||||||
|
|
||||||
|
/** Returns the singleton empty cursor instance. */
|
||||||
|
@SuppressWarnings("unchecked")
|
||||||
|
public static <K, V> MapCursor<K, V> instance() {
|
||||||
|
return (MapCursor<K, V>) INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean advance() {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public K getKey() {
|
||||||
|
throw new IllegalStateException("Cannot get key from empty cursor");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public V getValue() {
|
||||||
|
throw new IllegalStateException("Cannot get value from empty cursor");
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,40 @@
|
|||||||
|
/*
|
||||||
|
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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
|
||||||
|
*
|
||||||
|
* https://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 org.pkl.core.util;
|
||||||
|
|
||||||
|
/** A cursor for iterating over map entries without allocating Entry objects. */
|
||||||
|
public interface MapCursor<K, V> {
|
||||||
|
/**
|
||||||
|
* Advances the cursor to the next entry.
|
||||||
|
*
|
||||||
|
* @return true if there is a next entry, false if iteration is complete
|
||||||
|
*/
|
||||||
|
boolean advance();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the key at the current cursor position.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if called before {@link #advance()} or after it returns false
|
||||||
|
*/
|
||||||
|
K getKey();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the value at the current cursor position.
|
||||||
|
*
|
||||||
|
* @throws IllegalStateException if called before {@link #advance()} or after it returns false
|
||||||
|
*/
|
||||||
|
V getValue();
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user