diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java index 7bca213d1..092727185 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmListingOrMapping.java @@ -16,12 +16,14 @@ package org.pkl.core.runtime; import com.oracle.truffle.api.CompilerDirectives; +import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.MaterializedFrame; import com.oracle.truffle.api.nodes.IndirectCallNode; import org.graalvm.collections.UnmodifiableEconomicMap; import org.pkl.core.ast.member.ListingOrMappingTypeCastNode; import org.pkl.core.ast.member.ObjectMember; import org.pkl.core.ast.type.TypeNode; +import org.pkl.core.util.EconomicMaps; import org.pkl.core.util.Nullable; public abstract class VmListingOrMapping extends VmObject { @@ -51,7 +53,7 @@ public final Object doTypeCast( var result = this == owner ? value - : ((VmListingOrMapping) this.parent) + : ((VmListingOrMapping) parent) .doTypeCast(value, owner, callNode, member, newNextTypeCastNode); if (typeCastNode == null || typeCastNode == nextTypeCastNode) return result; var callTarget = typeCastNode.getCallTarget(); @@ -66,6 +68,34 @@ public final Object doTypeCast( } } + @Override + @TruffleBoundary + public final @Nullable Object getCachedValue(Object key) { + var result = EconomicMaps.get(cachedValues, key); + if (result != null || !members.isEmpty()) return result; + + // optimization: steal value from parent cache to avoid computing it multiple times + + assert parent != null; // VmListingOrMapping always has a parent + result = parent.getCachedValue(key); + if (result == null) return null; + + if (typeCastNode != null) { + var callNode = IndirectCallNode.getUncached(); + var callTarget = typeCastNode.getCallTarget(); + try { + result = callNode.call(callTarget, getEnclosingReceiver(), getEnclosingOwner(), result); + } catch (VmException e) { + var member = VmUtils.findMember(parent, key); + assert member != null; // already found the member's cached value + VmUtils.insertStackFrame(member, callTarget, e); + throw e; + } + } + setCachedValue(key, result); + return result; + } + /** * Tells whether the reified element/value type of this listing/mapping is known to be a subtype * of {@code typeNode}. (If {@code true}, it is redundant to check that elements/values have type diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmObject.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmObject.java index 51d497b09..3987d9acc 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmObject.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmObject.java @@ -82,7 +82,7 @@ public final UnmodifiableEconomicMap getMembers() { } @Override - public @Nullable final Object getCachedValue(Object key) { + public @Nullable Object getCachedValue(Object key) { return EconomicMaps.get(cachedValues, key); } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing7.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing7.pkl new file mode 100644 index 000000000..47e6a843e --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/listings/listing7.pkl @@ -0,0 +1,10 @@ +// ensure that these members are only evaluated once (trace should only be emitted once) +listing = new Listing { trace(1) } + +listing2: Listing = listing + +listing3 = new Listing { + new Listing { trace(2) } +} + +listing4: Listing> = listing3 diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing7.err b/pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing7.err new file mode 100644 index 000000000..ad24c7ed5 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/listings/listing7.err @@ -0,0 +1,18 @@ +listing { + 1 +} +listing2 { + 1 +} +listing3 { + new { + 2 + } +} +listing4 { + new { + 2 + } +} +pkl: TRACE: 1 = 1 (file:///$snippetsDir/input/listings/listing7.pkl) +pkl: TRACE: 2 = 2 (file:///$snippetsDir/input/listings/listing7.pkl)