mirror of
https://github.com/apple/pkl.git
synced 2026-09-09 19:31:51 +02:00
Improve partial-evaluation when amending null parents (#1836)
GraalVM's partial evaulator can't handle recursive calls into the same node. The current implementation around evaluating `VmNull` as a parent causes the partial evaluator to bail out, leaving Pkl stuck in interpreter mode. This rewrites the various object literals to add individual specializations for each type of default value we can see from a VmNull parent. Also: * Fix `isTypeObjectClass` impl * Fix bug when amending with generator object literal node with object params
This commit is contained in:
+59
-9
@@ -17,6 +17,7 @@ package org.pkl.core.ast.expression.generator;
|
|||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
|
import com.oracle.truffle.api.dsl.Bind;
|
||||||
import com.oracle.truffle.api.dsl.Cached;
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
import com.oracle.truffle.api.dsl.Fallback;
|
import com.oracle.truffle.api.dsl.Fallback;
|
||||||
import com.oracle.truffle.api.dsl.Idempotent;
|
import com.oracle.truffle.api.dsl.Idempotent;
|
||||||
@@ -34,7 +35,7 @@ import org.pkl.core.ast.type.UnresolvedTypeNode;
|
|||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
|
|
||||||
/** An object literal node that contains at least one for- or when-expression. */
|
/** An object literal node that contains at least one for- or when-expression. */
|
||||||
@ImportStatic(BaseModule.class)
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
||||||
@Children private final GeneratorMemberNode[] memberNodes;
|
@Children private final GeneratorMemberNode[] memberNodes;
|
||||||
|
|
||||||
@@ -80,6 +81,20 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
return data.storeGeneratorFrames(result);
|
return data.storeGeneratorFrames(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isDynamicClass()", "checkObjectCannotHaveParameters()"})
|
||||||
|
protected Object evalNullWithDynamicDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
var parentDynamic = (VmDynamic) defaultValue;
|
||||||
|
var data = executeChildren(frame, parentDynamic, parentDynamic.getLength());
|
||||||
|
if (data.hasNoMembers()) {
|
||||||
|
return parentDynamic;
|
||||||
|
}
|
||||||
|
var result = new VmDynamic(frame.materialize(), parentDynamic, data.members(), data.length());
|
||||||
|
return data.storeGeneratorFrames(result);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
||||||
protected VmTyped evalTyped(VirtualFrame frame, VmTyped parent) {
|
protected VmTyped evalTyped(VirtualFrame frame, VmTyped parent) {
|
||||||
VmUtils.checkIsInstantiable(parent.getVmClass(), getParentNode());
|
VmUtils.checkIsInstantiable(parent.getVmClass(), getParentNode());
|
||||||
@@ -91,6 +106,14 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), data.members());
|
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), data.members());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isTypedObjectClass(getClass(defaultValue))", "checkObjectCannotHaveParameters()"})
|
||||||
|
protected Object evalNullWithTypedDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalTyped(frame, (VmTyped) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkListingCannotHaveParameters()")
|
@Specialization(guards = "checkListingCannotHaveParameters()")
|
||||||
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
||||||
var data = executeChildren(frame, parent, parent.getLength());
|
var data = executeChildren(frame, parent, parent.getLength());
|
||||||
@@ -101,6 +124,14 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
return data.storeGeneratorFrames(result);
|
return data.storeGeneratorFrames(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isListingClass()", "checkListingCannotHaveParameters()"})
|
||||||
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListing(frame, (VmListing) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkMappingCannotHaveParameters()")
|
@Specialization(guards = "checkMappingCannotHaveParameters()")
|
||||||
protected VmMapping evalMapping(VirtualFrame frame, VmMapping parent) {
|
protected VmMapping evalMapping(VirtualFrame frame, VmMapping parent) {
|
||||||
var data = executeChildren(frame, parent, 0);
|
var data = executeChildren(frame, parent, 0);
|
||||||
@@ -111,10 +142,12 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
return data.storeGeneratorFrames(result);
|
return data.storeGeneratorFrames(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
guards = {"getClass(defaultValue).isMappingClass()", "checkMappingCannotHaveParameters()"})
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
protected Object evalNullWithMappingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalMapping(frame, (VmMapping) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||||
@@ -127,6 +160,18 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = {"parent == getDynamicClass()", "checkObjectCannotHaveParameters()"})
|
@Specialization(guards = {"parent == getDynamicClass()", "checkObjectCannotHaveParameters()"})
|
||||||
protected VmDynamic evalDynamicClass(VirtualFrame frame, VmClass parent) {
|
protected VmDynamic evalDynamicClass(VirtualFrame frame, VmClass parent) {
|
||||||
var data = executeChildren(frame, parent, 0);
|
var data = executeChildren(frame, parent, 0);
|
||||||
@@ -173,8 +218,13 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
@Fallback
|
@Fallback
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected void fallback(Object parent) {
|
protected void fallback(Object parent) {
|
||||||
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
VmUtils.checkIsInstantiable(
|
VmUtils.checkIsInstantiable(
|
||||||
parent instanceof VmClass vmClass ? vmClass : VmUtils.getClass(parent), getParentNode());
|
value instanceof VmClass vmClass ? vmClass : VmUtils.getClass(value), getParentNode());
|
||||||
|
|
||||||
throw exceptionBuilder().unreachableCode().build();
|
throw exceptionBuilder().unreachableCode().build();
|
||||||
}
|
}
|
||||||
@@ -186,7 +236,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("objectAmendmentCannotHaveParameters")
|
.evalError("objectAmendmentCannotHaveParameters")
|
||||||
.withLocation(parameterTypes[0])
|
.withLocation(getParentNode())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -197,7 +247,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("listingAmendmentCannotHaveParameters")
|
.evalError("listingAmendmentCannotHaveParameters")
|
||||||
.withLocation(parameterTypes[0])
|
.withLocation(getParentNode())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -208,7 +258,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
|||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("mappingAmendmentCannotHaveParameters")
|
.evalError("mappingAmendmentCannotHaveParameters")
|
||||||
.withLocation(parameterTypes[0])
|
.withLocation(getParentNode())
|
||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+40
-6
@@ -33,7 +33,7 @@ import org.pkl.core.runtime.*;
|
|||||||
* all entry keys are constants. Example: `new foo { ["one"] = 1 }`
|
* all entry keys are constants. Example: `new foo { ["one"] = 1 }`
|
||||||
*/
|
*/
|
||||||
// IDEA: don't materialize frames if all members have constant values
|
// IDEA: don't materialize frames if all members have constant values
|
||||||
@ImportStatic(BaseModule.class)
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class ConstantEntriesLiteralNode extends SpecializedObjectLiteralNode {
|
public abstract class ConstantEntriesLiteralNode extends SpecializedObjectLiteralNode {
|
||||||
public ConstantEntriesLiteralNode(
|
public ConstantEntriesLiteralNode(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
@@ -73,21 +73,38 @@ public abstract class ConstantEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
return new VmMapping(frame.materialize(), parent, members);
|
return new VmMapping(frame.materialize(), parent, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isMappingClass()", "checkIsValidMappingAmendment()"})
|
||||||
|
protected Object evalNullWithMappingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalMapping(frame, (VmMapping) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
||||||
return new VmDynamic(frame.materialize(), parent, members, parent.getLength());
|
return new VmDynamic(frame.materialize(), parent, members, parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(guards = "getClass(defaultValue).isDynamicClass()")
|
||||||
|
protected Object evalNullWithDynamicDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalDynamic(frame, (VmDynamic) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidListingAmendment()")
|
@Specialization(guards = "checkIsValidListingAmendment()")
|
||||||
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
||||||
checkMaxListingMemberIndex(parent.getLength());
|
checkMaxListingMemberIndex(parent.getLength());
|
||||||
return new VmListing(frame.materialize(), parent, members, parent.getLength());
|
return new VmListing(frame.materialize(), parent, members, parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
guards = {"getClass(defaultValue).isListingClass()", "checkIsValidListingAmendment()"})
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListing(frame, (VmListing) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||||
@@ -100,6 +117,18 @@ public abstract class ConstantEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = {"parent == getMappingClass()", "checkIsValidMappingAmendment()"})
|
@Specialization(guards = {"parent == getMappingClass()", "checkIsValidMappingAmendment()"})
|
||||||
protected VmMapping evalMappingClass(
|
protected VmMapping evalMappingClass(
|
||||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||||
@@ -127,6 +156,11 @@ public abstract class ConstantEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
@Fallback
|
@Fallback
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected void fallback(Object parent) {
|
protected void fallback(Object parent) {
|
||||||
elementsEntriesFallback(parent, findFirstNonProperty(members), false);
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
|
elementsEntriesFallback(value, findFirstNonProperty(members), false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+33
-6
@@ -16,6 +16,7 @@
|
|||||||
package org.pkl.core.ast.expression.literal;
|
package org.pkl.core.ast.expression.literal;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
|
import com.oracle.truffle.api.dsl.Bind;
|
||||||
import com.oracle.truffle.api.dsl.Cached;
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
import com.oracle.truffle.api.dsl.Fallback;
|
import com.oracle.truffle.api.dsl.Fallback;
|
||||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||||
@@ -36,7 +37,7 @@ import org.pkl.core.util.EconomicMaps;
|
|||||||
* Object literal that contains both elements and entries (and possibly properties). Example: `new
|
* Object literal that contains both elements and entries (and possibly properties). Example: `new
|
||||||
* foo { "pigeon", [3] = "barn owl" }`
|
* foo { "pigeon", [3] = "barn owl" }`
|
||||||
*/
|
*/
|
||||||
@ImportStatic(BaseModule.class)
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLiteralNode {
|
public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLiteralNode {
|
||||||
private final ObjectMember[] elements;
|
private final ObjectMember[] elements;
|
||||||
@Children private final ExpressionNode[] keyNodes;
|
@Children private final ExpressionNode[] keyNodes;
|
||||||
@@ -95,6 +96,14 @@ public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
parent.getLength() + elements.length);
|
parent.getLength() + elements.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isListingClass()", "checkIsValidListingAmendment()"})
|
||||||
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListing(frame, (VmListing) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
||||||
return new VmDynamic(
|
return new VmDynamic(
|
||||||
@@ -104,10 +113,11 @@ public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
parent.getLength() + elements.length);
|
parent.getLength() + elements.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(guards = "getClass(defaultValue).isDynamicClass()")
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
protected Object evalNullWithDynamicDefault(
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalDynamic(frame, (VmDynamic) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||||
@@ -120,6 +130,18 @@ public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = {"parent == getListingClass()", "checkIsValidListingAmendment()"})
|
@Specialization(guards = {"parent == getListingClass()", "checkIsValidListingAmendment()"})
|
||||||
protected VmListing evalListingClass(
|
protected VmListing evalListingClass(
|
||||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||||
@@ -145,7 +167,12 @@ public abstract class ElementsEntriesLiteralNode extends SpecializedObjectLitera
|
|||||||
@Fallback
|
@Fallback
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected void fallback(Object parent) {
|
protected void fallback(Object parent) {
|
||||||
elementsEntriesFallback(parent, elements[0], true);
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
|
elementsEntriesFallback(value, elements[0], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
|
|||||||
+32
-6
@@ -32,7 +32,7 @@ import org.pkl.core.util.EconomicMaps;
|
|||||||
* Object literal that contains elements (and possibly properties) but not entries. Example: `new
|
* Object literal that contains elements (and possibly properties) but not entries. Example: `new
|
||||||
* foo { "pigeon" }`
|
* foo { "pigeon" }`
|
||||||
*/
|
*/
|
||||||
@ImportStatic(BaseModule.class)
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class ElementsLiteralNode extends SpecializedObjectLiteralNode {
|
public abstract class ElementsLiteralNode extends SpecializedObjectLiteralNode {
|
||||||
private final ObjectMember[] elements;
|
private final ObjectMember[] elements;
|
||||||
|
|
||||||
@@ -94,10 +94,11 @@ public abstract class ElementsLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
parent.getLength() + elements.length);
|
parent.getLength() + elements.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(guards = "getClass(defaultValue).isDynamicClass()")
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
protected Object evalNullWithDynamicDefault(
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalDynamicUncached(frame, (VmDynamic) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||||
@@ -110,6 +111,18 @@ public abstract class ElementsLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(
|
@Specialization(
|
||||||
guards = {
|
guards = {
|
||||||
"parent == getListingClass()",
|
"parent == getListingClass()",
|
||||||
@@ -162,10 +175,23 @@ public abstract class ElementsLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
parent.getLength() + elements.length);
|
parent.getLength() + elements.length);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isListingClass()", "checkIsValidListingAmendment()"})
|
||||||
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListingUncached(frame, (VmListing) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Fallback
|
@Fallback
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected void fallback(Object parent) {
|
protected void fallback(Object parent) {
|
||||||
elementsEntriesFallback(parent, elements[0], true);
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
|
elementsEntriesFallback(value, elements[0], true);
|
||||||
}
|
}
|
||||||
|
|
||||||
// offset element keys according to parentLength
|
// offset element keys according to parentLength
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ import org.pkl.core.util.EconomicMaps;
|
|||||||
* used.) Example: `foo { ["on" + "e"] = 1 }`
|
* used.) Example: `foo { ["on" + "e"] = 1 }`
|
||||||
*/
|
*/
|
||||||
// IDEA: don't materialize frames if all members have constant values
|
// IDEA: don't materialize frames if all members have constant values
|
||||||
@ImportStatic(BaseModule.class)
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
||||||
@Children private final ExpressionNode[] keyNodes;
|
@Children private final ExpressionNode[] keyNodes;
|
||||||
private final ObjectMember[] values;
|
private final ObjectMember[] values;
|
||||||
@@ -92,11 +92,26 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
return new VmMapping(frame.materialize(), parent, createMapMembers(frame));
|
return new VmMapping(frame.materialize(), parent, createMapMembers(frame));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isMappingClass()", "checkIsValidMappingAmendment()"})
|
||||||
|
protected Object evalNullWithMappingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalMapping(frame, (VmMapping) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
||||||
return new VmDynamic(frame.materialize(), parent, createMapMembers(frame), parent.getLength());
|
return new VmDynamic(frame.materialize(), parent, createMapMembers(frame), parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(guards = {"getClass(defaultValue).isDynamicClass()"})
|
||||||
|
protected Object evalNullWithDynamicDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalDynamic(frame, (VmDynamic) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidListingAmendment()")
|
@Specialization(guards = "checkIsValidListingAmendment()")
|
||||||
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
||||||
return new VmListing(
|
return new VmListing(
|
||||||
@@ -107,10 +122,12 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
parent.getLength());
|
parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
guards = {"getClass(defaultValue).isListingClass()", "checkIsValidListingAmendment()"})
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListing(frame, (VmListing) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||||
@@ -123,6 +140,18 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = {"parent == getMappingClass()", "checkIsValidMappingAmendment()"})
|
@Specialization(guards = {"parent == getMappingClass()", "checkIsValidMappingAmendment()"})
|
||||||
protected VmMapping evalMappingClass(
|
protected VmMapping evalMappingClass(
|
||||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||||
@@ -152,7 +181,12 @@ public abstract class EntriesLiteralNode extends SpecializedObjectLiteralNode {
|
|||||||
@Fallback
|
@Fallback
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected Object fallback(Object parent) {
|
protected Object fallback(Object parent) {
|
||||||
return elementsEntriesFallback(parent, values[0], false);
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
|
return elementsEntriesFallback(value, values[0], false);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
|
|||||||
@@ -20,14 +20,17 @@ import com.oracle.truffle.api.dsl.Idempotent;
|
|||||||
import com.oracle.truffle.api.dsl.NodeChild;
|
import com.oracle.truffle.api.dsl.NodeChild;
|
||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
|
import com.oracle.truffle.api.nodes.LoopNode;
|
||||||
import com.oracle.truffle.api.source.SourceSection;
|
import com.oracle.truffle.api.source.SourceSection;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.ast.type.TypeNode;
|
import org.pkl.core.ast.type.TypeNode;
|
||||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||||
|
import org.pkl.core.runtime.BaseModule;
|
||||||
import org.pkl.core.runtime.VmClass;
|
import org.pkl.core.runtime.VmClass;
|
||||||
import org.pkl.core.runtime.VmFunction;
|
import org.pkl.core.runtime.VmFunction;
|
||||||
import org.pkl.core.runtime.VmLanguage;
|
import org.pkl.core.runtime.VmLanguage;
|
||||||
|
import org.pkl.core.runtime.VmNull;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
import org.pkl.core.runtime.VmUtils;
|
||||||
|
|
||||||
// IDEA: don't materialize frames when all members are constants
|
// IDEA: don't materialize frames when all members are constants
|
||||||
@@ -57,8 +60,6 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
|
|||||||
|
|
||||||
protected abstract ExpressionNode getParentNode();
|
protected abstract ExpressionNode getParentNode();
|
||||||
|
|
||||||
protected abstract Object executeWithParent(VirtualFrame frame, Object parent);
|
|
||||||
|
|
||||||
protected abstract ObjectLiteralNode copy(ExpressionNode newParentNode);
|
protected abstract ObjectLiteralNode copy(ExpressionNode newParentNode);
|
||||||
|
|
||||||
protected final AmendFunctionNode createAmendFunctionNode(VirtualFrame frame) {
|
protected final AmendFunctionNode createAmendFunctionNode(VirtualFrame frame) {
|
||||||
@@ -71,7 +72,14 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
|
|||||||
|
|
||||||
@Idempotent
|
@Idempotent
|
||||||
protected static boolean isTypedObjectClass(VmClass clazz) {
|
protected static boolean isTypedObjectClass(VmClass clazz) {
|
||||||
return !(clazz.isListingClass() || clazz.isMappingClass() || clazz.isDynamicClass());
|
if (clazz.isListingClass()
|
||||||
|
|| clazz.isMappingClass()
|
||||||
|
|| clazz.isDynamicClass()
|
||||||
|
|| clazz.isFunctionClass()
|
||||||
|
|| clazz.isFunctionNClass()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return BaseModule.getTypedClass().isSuperclassOf(clazz);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected final boolean checkIsValidFunctionAmendment(VmFunction parent) {
|
protected final boolean checkIsValidFunctionAmendment(VmFunction parent) {
|
||||||
@@ -85,4 +93,25 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
|
|||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
|
protected final boolean checkIsValidFunctionAmendment(Object parent) {
|
||||||
|
return checkIsValidFunctionAmendment((VmFunction) parent);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
|
protected final boolean isFunction(Object value) {
|
||||||
|
return value instanceof VmFunction;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected final Object getNullDefaultValue(VmNull parent) {
|
||||||
|
var value = parent.getDefaultValue();
|
||||||
|
var count = 0;
|
||||||
|
while (value instanceof VmNull n) {
|
||||||
|
value = n.getDefaultValue();
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
LoopNode.reportLoopCount(this, count);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+76
-5
@@ -16,7 +16,9 @@
|
|||||||
package org.pkl.core.ast.expression.literal;
|
package org.pkl.core.ast.expression.literal;
|
||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
|
import com.oracle.truffle.api.dsl.Bind;
|
||||||
import com.oracle.truffle.api.dsl.Cached;
|
import com.oracle.truffle.api.dsl.Cached;
|
||||||
|
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||||
import com.oracle.truffle.api.dsl.Specialization;
|
import com.oracle.truffle.api.dsl.Specialization;
|
||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
@@ -31,6 +33,7 @@ import org.pkl.core.runtime.*;
|
|||||||
|
|
||||||
/** Object literal that contains properties but not elements or entries. */
|
/** Object literal that contains properties but not elements or entries. */
|
||||||
// IDEA: don't materialize frame when all members are constants
|
// IDEA: don't materialize frame when all members are constants
|
||||||
|
@ImportStatic({BaseModule.class, VmUtils.class})
|
||||||
public abstract class PropertiesLiteralNode extends SpecializedObjectLiteralNode {
|
public abstract class PropertiesLiteralNode extends SpecializedObjectLiteralNode {
|
||||||
public PropertiesLiteralNode(
|
public PropertiesLiteralNode(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
@@ -82,26 +85,77 @@ public abstract class PropertiesLiteralNode extends SpecializedObjectLiteralNode
|
|||||||
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), members);
|
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), members);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {
|
||||||
|
"parentClass == getClass(defaultValue)",
|
||||||
|
"isTypedObjectClass(parentClass)",
|
||||||
|
"checkIsValidTypedAmendment(parentClass)"
|
||||||
|
})
|
||||||
|
protected Object evalNullWithTypedObjectDefaultCached(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached("getClass(defaultValue)") VmClass parentClass) {
|
||||||
|
var parentTyped = (VmTyped) defaultValue;
|
||||||
|
return new VmTyped(frame.materialize(), parentTyped, parentTyped.getVmClass(), members);
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {
|
||||||
|
"isTypedObjectClass(getClass(defaultValue))",
|
||||||
|
"checkIsValidTypedAmendment(defaultValue)"
|
||||||
|
})
|
||||||
|
protected Object evalNullWithTypedObjectDefaultUncached(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind(value = "getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
var parentTyped = (VmTyped) defaultValue;
|
||||||
|
return new VmTyped(frame.materialize(), parentTyped, parentTyped.getVmClass(), members);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization
|
@Specialization
|
||||||
protected Object evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
protected Object evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
||||||
return new VmDynamic(frame.materialize(), parent, members, parent.getLength());
|
return new VmDynamic(frame.materialize(), parent, members, parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(guards = "getClass(defaultValue).isDynamicClass()")
|
||||||
|
protected Object evalNullWithDynamicDefault(
|
||||||
|
VirtualFrame frame, VmNull parent, @Bind("getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalDynamic(frame, (VmDynamic) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(guards = "checkIsValidListingAmendment()")
|
@Specialization(guards = "checkIsValidListingAmendment()")
|
||||||
protected Object evalListing(VirtualFrame frame, VmListing parent) {
|
protected Object evalListing(VirtualFrame frame, VmListing parent) {
|
||||||
return new VmListing(frame.materialize(), parent, members, parent.getLength());
|
return new VmListing(frame.materialize(), parent, members, parent.getLength());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"getClass(defaultValue).isListingClass()", "checkIsValidListingAmendment()"})
|
||||||
|
protected Object evalNullWithListingDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind(value = "getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalListing(frame, (VmListing) defaultValue);
|
||||||
|
}
|
||||||
|
|
||||||
@ExplodeLoop
|
@ExplodeLoop
|
||||||
@Specialization(guards = "checkIsValidMappingAmendment()")
|
@Specialization(guards = "checkIsValidMappingAmendment()")
|
||||||
protected Object evalMapping(VirtualFrame frame, VmMapping parent) {
|
protected Object evalMapping(VirtualFrame frame, VmMapping parent) {
|
||||||
return new VmMapping(frame.materialize(), parent, members);
|
return new VmMapping(frame.materialize(), parent, members);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Specialization
|
@SuppressWarnings("unused")
|
||||||
protected Object evalNull(VirtualFrame frame, VmNull parent) {
|
@Specialization(
|
||||||
// assumes that Graal PE can handle recursive call to same node
|
guards = {"getClass(defaultValue).isMappingClass()", "checkIsValidMappingAmendment()"})
|
||||||
return executeWithParent(frame, parent.getDefaultValue());
|
protected Object evalNullWithMappingDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind(value = "getNullDefaultValue(parent)") Object defaultValue) {
|
||||||
|
return evalMapping(frame, (VmMapping) defaultValue);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ultimately, this lambda or a lambda returned from it will call one of the other
|
// Ultimately, this lambda or a lambda returned from it will call one of the other
|
||||||
@@ -121,6 +175,18 @@ public abstract class PropertiesLiteralNode extends SpecializedObjectLiteralNode
|
|||||||
return amendFunctionNode.execute(frame, parent);
|
return amendFunctionNode.execute(frame, parent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("unused")
|
||||||
|
@Specialization(
|
||||||
|
guards = {"isFunction(defaultValue)", "checkIsValidFunctionAmendment(defaultValue)"})
|
||||||
|
protected Object evalNullWithFunctionDefault(
|
||||||
|
VirtualFrame frame,
|
||||||
|
VmNull parent,
|
||||||
|
@Bind("getNullDefaultValue(parent)") Object defaultValue,
|
||||||
|
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||||
|
AmendFunctionNode amendFunctionNode) {
|
||||||
|
return evalFunction(frame, (VmFunction) defaultValue, amendFunctionNode);
|
||||||
|
}
|
||||||
|
|
||||||
@Specialization(
|
@Specialization(
|
||||||
guards = {
|
guards = {
|
||||||
"parent == cachedParent",
|
"parent == cachedParent",
|
||||||
@@ -199,8 +265,13 @@ public abstract class PropertiesLiteralNode extends SpecializedObjectLiteralNode
|
|||||||
@Specialization
|
@Specialization
|
||||||
@TruffleBoundary
|
@TruffleBoundary
|
||||||
protected void fallback(Object parent) {
|
protected void fallback(Object parent) {
|
||||||
|
var value = parent;
|
||||||
|
// blame the non-null type (e.g. blame `Duration` instead of `Null` in the case of `Duration?`)
|
||||||
|
if (value instanceof VmNull vmNull) {
|
||||||
|
value = getNullDefaultValue(vmNull);
|
||||||
|
}
|
||||||
// should always throw
|
// should always throw
|
||||||
checkIsValidTypedAmendment(parent);
|
checkIsValidTypedAmendment(value);
|
||||||
|
|
||||||
throw exceptionBuilder().unreachableCode().build();
|
throw exceptionBuilder().unreachableCode().build();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ import static org.pkl.core.PClassInfo.pklBaseUri;
|
|||||||
|
|
||||||
import com.oracle.truffle.api.CompilerDirectives;
|
import com.oracle.truffle.api.CompilerDirectives;
|
||||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||||
|
import com.oracle.truffle.api.dsl.Idempotent;
|
||||||
|
|
||||||
public final class BaseModule extends StdLibModule {
|
public final class BaseModule extends StdLibModule {
|
||||||
static final VmTyped instance = VmUtils.createEmptyModule();
|
static final VmTyped instance = VmUtils.createEmptyModule();
|
||||||
@@ -91,6 +92,7 @@ public final class BaseModule extends StdLibModule {
|
|||||||
return SetClass.instance;
|
return SetClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
public static VmClass getListingClass() {
|
public static VmClass getListingClass() {
|
||||||
return ListingClass.instance;
|
return ListingClass.instance;
|
||||||
}
|
}
|
||||||
@@ -99,10 +101,12 @@ public final class BaseModule extends StdLibModule {
|
|||||||
return MapClass.instance;
|
return MapClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
public static VmClass getMappingClass() {
|
public static VmClass getMappingClass() {
|
||||||
return MappingClass.instance;
|
return MappingClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
public static VmClass getDynamicClass() {
|
public static VmClass getDynamicClass() {
|
||||||
return DynamicClass.instance;
|
return DynamicClass.instance;
|
||||||
}
|
}
|
||||||
@@ -135,6 +139,7 @@ public final class BaseModule extends StdLibModule {
|
|||||||
return RegexMatchClass.instance;
|
return RegexMatchClass.instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Idempotent
|
||||||
public static VmClass getFunctionClass() {
|
public static VmClass getFunctionClass() {
|
||||||
return FunctionClass.instance;
|
return FunctionClass.instance;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
|||||||
import com.oracle.truffle.api.Truffle;
|
import com.oracle.truffle.api.Truffle;
|
||||||
import com.oracle.truffle.api.TruffleLanguage;
|
import com.oracle.truffle.api.TruffleLanguage;
|
||||||
import com.oracle.truffle.api.TruffleStackTrace;
|
import com.oracle.truffle.api.TruffleStackTrace;
|
||||||
|
import com.oracle.truffle.api.dsl.Idempotent;
|
||||||
import com.oracle.truffle.api.frame.*;
|
import com.oracle.truffle.api.frame.*;
|
||||||
import com.oracle.truffle.api.nodes.*;
|
import com.oracle.truffle.api.nodes.*;
|
||||||
import com.oracle.truffle.api.source.Source;
|
import com.oracle.truffle.api.source.Source;
|
||||||
@@ -528,6 +529,7 @@ public final class VmUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// implements same behavior as AnyNodes#getClass
|
// implements same behavior as AnyNodes#getClass
|
||||||
|
@Idempotent
|
||||||
public static VmClass getClass(Object value) {
|
public static VmClass getClass(Object value) {
|
||||||
if (value instanceof VmValue vmValue) {
|
if (value instanceof VmValue vmValue) {
|
||||||
return vmValue.getVmClass();
|
return vmValue.getVmClass();
|
||||||
|
|||||||
+9
@@ -0,0 +1,9 @@
|
|||||||
|
class Person { name: String }
|
||||||
|
|
||||||
|
hidden x: Mixin<Person>?
|
||||||
|
|
||||||
|
local y = (x) {
|
||||||
|
name = "Fred"
|
||||||
|
}
|
||||||
|
|
||||||
|
res = y.apply(new Person {})
|
||||||
@@ -26,6 +26,25 @@ res2 = new Listing {
|
|||||||
new Mapping { ["one"] = 1; ["two"] = 2 }
|
new Mapping { ["one"] = 1; ["two"] = 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res2n = (Null(new Listing {})) {
|
||||||
|
null
|
||||||
|
true
|
||||||
|
42
|
||||||
|
1.23
|
||||||
|
"foo"
|
||||||
|
Regex("bar")
|
||||||
|
5.gb
|
||||||
|
3.min
|
||||||
|
Pair(1, 2)
|
||||||
|
List(1, 2, 3)
|
||||||
|
Set(1, 2, 3)
|
||||||
|
Map("one", 1, "two", 2)
|
||||||
|
new Dynamic { name = "Pigeon"; age = 42 }
|
||||||
|
new Person { name = "Pigeon"; age = 42 }
|
||||||
|
new Listing { 1; 2; 3 }
|
||||||
|
new Mapping { ["one"] = 1; ["two"] = 2 }
|
||||||
|
}
|
||||||
|
|
||||||
res3 = new Listing {
|
res3 = new Listing {
|
||||||
id(null)
|
id(null)
|
||||||
id(true)
|
id(true)
|
||||||
@@ -44,3 +63,22 @@ res3 = new Listing {
|
|||||||
id(new Listing { 1; 2; 3 })
|
id(new Listing { 1; 2; 3 })
|
||||||
id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res3n = (Null(new Listing {})) {
|
||||||
|
id(null)
|
||||||
|
id(true)
|
||||||
|
id(42)
|
||||||
|
id(1.23)
|
||||||
|
id("foo")
|
||||||
|
id(Regex("bar"))
|
||||||
|
id(5.gb)
|
||||||
|
id(3.min)
|
||||||
|
id(Pair(1, 2))
|
||||||
|
id(List(1, 2, 3))
|
||||||
|
id(Set(1, 2, 3))
|
||||||
|
id(Map("one", 1, "two", 2))
|
||||||
|
id(new Dynamic { name = "Pigeon"; age = 42 })
|
||||||
|
id(new Person { name = "Pigeon"; age = 42 })
|
||||||
|
id(new Listing { 1; 2; 3 })
|
||||||
|
id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
||||||
|
}
|
||||||
|
|||||||
@@ -73,11 +73,20 @@ res8 = test.catch(() -> (x) { ["wrong type"] = "value" })
|
|||||||
res8a = test.catch(() -> (x) { [id("wrong type")] = id("value") })
|
res8a = test.catch(() -> (x) { [id("wrong type")] = id("value") })
|
||||||
|
|
||||||
res9 = test.catch(() -> new Listing { foo = "bar" })
|
res9 = test.catch(() -> new Listing { foo = "bar" })
|
||||||
|
res9n = test.catch(() -> (Null(new Listing {})) { foo = "bar" })
|
||||||
res10 = test.catch(() -> (x) { foo = "foo" })
|
res10 = test.catch(() -> (x) { foo = "foo" })
|
||||||
|
res10n = test.catch(() -> (Null(x)) { foo = "foo" })
|
||||||
res11 = test.catch(() -> new Listing { "one"; foo = "foo" })
|
res11 = test.catch(() -> new Listing { "one"; foo = "foo" })
|
||||||
|
res11n = test.catch(() -> (Null(new Listing { "one" })) { foo = "foo" })
|
||||||
res12 = test.catch(() -> (x) { "four"; foo = "foo" })
|
res12 = test.catch(() -> (x) { "four"; foo = "foo" })
|
||||||
|
res12n = test.catch(() -> (Null(x)) { "four"; foo = "foo" })
|
||||||
res12a = test.catch(() -> (x) { id("four"); foo = "foo" })
|
res12a = test.catch(() -> (x) { id("four"); foo = "foo" })
|
||||||
|
res12an = test.catch(() -> (Null(x)) { id("four"); foo = "foo" })
|
||||||
res13 = test.catch(() -> (x) { [1] = "updated two"; foo = "foo" })
|
res13 = test.catch(() -> (x) { [1] = "updated two"; foo = "foo" })
|
||||||
|
res13n = test.catch(() -> (Null(x)) { [1] = "updated two"; foo = "foo" })
|
||||||
res13a = test.catch(() -> (x) { [id(1)] = id("updated two"); foo = "foo" })
|
res13a = test.catch(() -> (x) { [id(1)] = id("updated two"); foo = "foo" })
|
||||||
|
res13an = test.catch(() -> (Null(x)) { [id(1)] = id("updated two"); foo = "foo" })
|
||||||
res14 = test.catch(() -> (x) { "four"; [1] = "updated two"; foo = "foo" })
|
res14 = test.catch(() -> (x) { "four"; [1] = "updated two"; foo = "foo" })
|
||||||
|
res14n = test.catch(() -> (Null(x)) { "four"; [1] = "updated two"; foo = "foo" })
|
||||||
res14a = test.catch(() -> (x) { id("four"); [id(1)] = id("updated two"); foo = "foo" })
|
res14a = test.catch(() -> (x) { id("four"); [id(1)] = id("updated two"); foo = "foo" })
|
||||||
|
res14an = test.catch(() -> (Null(x)) { id("four"); [id(1)] = id("updated two"); foo = "foo" })
|
||||||
|
|||||||
@@ -26,6 +26,25 @@ res2 = new Mapping {
|
|||||||
[new Mapping { ["one"] = 1; ["two"] = 2 }] = new Mapping { ["one"] = 1; ["two"] = 2 }
|
[new Mapping { ["one"] = 1; ["two"] = 2 }] = new Mapping { ["one"] = 1; ["two"] = 2 }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res2n = (Null(new Mapping {})) {
|
||||||
|
[null] = null
|
||||||
|
[true] = true
|
||||||
|
[42] = 42
|
||||||
|
[1.23] = 1.23
|
||||||
|
["foo"] = "foo"
|
||||||
|
[Regex("bar")] = Regex("bar")
|
||||||
|
[5.gb] = 5.gb
|
||||||
|
[3.min] = 3.min
|
||||||
|
[Pair(1, 2)] = Pair(1, 2)
|
||||||
|
[List(1, 2, 3)] = List(1, 2, 3)
|
||||||
|
[Set(1, 2, 3)] = Set(1, 2, 3)
|
||||||
|
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
|
||||||
|
[new Dynamic { name = "Pigeon"; age = 42 }] = new Dynamic { name = "Pigeon"; age = 42 }
|
||||||
|
[new Person { name = "Pigeon"; age = 42 }] = new Person { name = "Pigeon"; age = 42 }
|
||||||
|
[new Listing { 1; 2; 3 }] = new Listing { 1; 2; 3 }
|
||||||
|
[new Mapping { ["one"] = 1; ["two"] = 2 }] = new Mapping { ["one"] = 1; ["two"] = 2 }
|
||||||
|
}
|
||||||
|
|
||||||
res3 = new Mapping {
|
res3 = new Mapping {
|
||||||
[id(null)] = id(null)
|
[id(null)] = id(null)
|
||||||
[id(true)] = id(true)
|
[id(true)] = id(true)
|
||||||
@@ -44,3 +63,34 @@ res3 = new Mapping {
|
|||||||
[id(new Listing { 1; 2; 3 })] = id(new Listing { 1; 2; 3 })
|
[id(new Listing { 1; 2; 3 })] = id(new Listing { 1; 2; 3 })
|
||||||
[id(new Mapping { ["one"] = 1; ["two"] = 2 })] = id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
[id(new Mapping { ["one"] = 1; ["two"] = 2 })] = id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
res3n = (Null(new Mapping {})) {
|
||||||
|
[id(null)] = id(null)
|
||||||
|
[id(true)] = id(true)
|
||||||
|
[id(42)] = id(42)
|
||||||
|
[id(1.23)] = id(1.23)
|
||||||
|
[id("foo")] = id("foo")
|
||||||
|
[id(Regex("bar"))] = id(Regex("bar"))
|
||||||
|
[id(5.gb)] = id(5.gb)
|
||||||
|
[id(3.min)] = id(3.min)
|
||||||
|
[id(Pair(1, 2))] = id(Pair(1, 2))
|
||||||
|
[id(List(1, 2, 3))] = id(List(1, 2, 3))
|
||||||
|
[id(Set(1, 2, 3))] = id(Set(1, 2, 3))
|
||||||
|
[id(Map("one", 1, "two", 2))] = id(Map("one", 1, "two", 2))
|
||||||
|
[id(new Dynamic { name = "Pigeon"; age = 42 })] = id(new Dynamic { name = "Pigeon"; age = 42 })
|
||||||
|
[id(new Person { name = "Pigeon"; age = 42 })] = id(new Person { name = "Pigeon"; age = 42 })
|
||||||
|
[id(new Listing { 1; 2; 3 })] = id(new Listing { 1; 2; 3 })
|
||||||
|
[id(new Mapping { ["one"] = 1; ["two"] = 2 })] = id(new Mapping { ["one"] = 1; ["two"] = 2 })
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstantEntriesLiteralNode
|
||||||
|
res4 = new Mapping {
|
||||||
|
["foo"] = "1"
|
||||||
|
["bar"] = "2"
|
||||||
|
}
|
||||||
|
|
||||||
|
// ConstantEntriesLiteralNode
|
||||||
|
res4n = (Null(new Mapping {})) {
|
||||||
|
["foo"] = "1"
|
||||||
|
["bar"] = "2"
|
||||||
|
}
|
||||||
|
|||||||
+47
@@ -0,0 +1,47 @@
|
|||||||
|
// Covers ConstantEntriesLiteralNode's specializations for amending a `VmNull` whose default value
|
||||||
|
// is each of the underlying parent types it supports. All entry keys below are constants, so
|
||||||
|
// these bodies compile to ConstantEntriesLiteralNode rather than EntriesLiteralNode.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
// evalNullWithMappingDefault
|
||||||
|
local mapping = Null(new Mapping { ["a"] = 1 })
|
||||||
|
res1 = (mapping) {
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { a = 1 })
|
||||||
|
res2 = (dynamic) {
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "a"; "b" })
|
||||||
|
res3 = (listing) {
|
||||||
|
[0] = "updated a"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Mapping { ["a"] = 1 })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res4 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: listing entry index out of range
|
||||||
|
res5 = test.catch(() -> (Null(new Listing { "a" })) { [5] = "value" })
|
||||||
|
|
||||||
|
// error: listing cannot have a property (other than `default`)
|
||||||
|
res6 = test.catch(() -> (Null(new Listing { "a" })) { [0] = "updated a"; foo = "bar" })
|
||||||
|
|
||||||
|
// error: mapping cannot have a property (other than `default`)
|
||||||
|
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { ["b"] = 2; foo = "bar" })
|
||||||
|
|
||||||
|
// error: function amendment with wrong parameter count
|
||||||
|
local twoArgFun = (x, y) -> new Mapping {}
|
||||||
|
res8 = test.catch(() -> (Null(twoArgFun)) { x -> ["a"] = 1 })
|
||||||
|
|
||||||
|
hidden foo: "foo"?
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res9 = test.catch(() -> (foo) { ["a"] = 1 })
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// Covers ElementsLiteralNode's specializations for amending a `VmNull` whose default value is
|
||||||
|
// each of the underlying parent types it supports.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { a = 1 })
|
||||||
|
res1 = (dynamic) {
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "a" })
|
||||||
|
res2 = (listing) {
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Listing { "a" })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res3 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: mapping cannot have an element
|
||||||
|
res4 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { "b" })
|
||||||
|
|
||||||
|
// error: listing cannot have a property (other than `default`)
|
||||||
|
res5 = test.catch(() -> (Null(new Listing { "a" })) { "b"; foo = "bar" })
|
||||||
|
|
||||||
|
// error: function amendment with wrong parameter count
|
||||||
|
local twoArgFun = (x, y) -> new Listing {}
|
||||||
|
res6 = test.catch(() -> (Null(twoArgFun)) { x -> "b" })
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res7 = test.catch(() -> (Null(42)) { "b" })
|
||||||
+45
@@ -0,0 +1,45 @@
|
|||||||
|
// Covers ElementsEntriesLiteralNode's specializations for amending a `VmNull` whose default value
|
||||||
|
// is each of the underlying parent types it supports. Element and entry keys are mixed, and at
|
||||||
|
// least one key is non-constant (via `id()`), matching this node's shape.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
function id(x) = x
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { "a" })
|
||||||
|
|
||||||
|
res1 = (dynamic) {
|
||||||
|
[id(0)] = "updated a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "a"; "b" })
|
||||||
|
res2 = (listing) {
|
||||||
|
"c"
|
||||||
|
[id(0)] = "updated a"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Listing { "a" })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
"b"
|
||||||
|
[id(0)] = "updated a"
|
||||||
|
}
|
||||||
|
res3 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: mapping cannot have an element
|
||||||
|
res4 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { "b"; [id("c")] = 2 })
|
||||||
|
|
||||||
|
// error: listing entry index out of range
|
||||||
|
res5 = test.catch(() -> (Null(new Listing { "a" })) { "b"; [id(5)] = "value" })
|
||||||
|
|
||||||
|
// error: listing cannot have a property (other than `default`)
|
||||||
|
res6 = test.catch(() -> (Null(new Listing { "a" })) { "b"; [id(0)] = "updated a"; foo = "bar" })
|
||||||
|
|
||||||
|
// error: function amendment with wrong parameter count
|
||||||
|
local twoArgFun = (x, y) -> new Listing {}
|
||||||
|
res7 = test.catch(() -> (Null(twoArgFun)) { x -> "b"; [id(0)] = "updated a" })
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res8 = test.catch(() -> (Null(42)) { "b"; [id(0)] = "updated a" })
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
// Covers EntriesLiteralNode's specializations for amending a `VmNull` whose default value is
|
||||||
|
// each of the underlying parent types it supports. At least one entry key below is non-constant
|
||||||
|
// (via `id()`) so that these bodies compile to EntriesLiteralNode rather than
|
||||||
|
// ConstantEntriesLiteralNode.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
function id(x) = x
|
||||||
|
|
||||||
|
// evalNullableMapping
|
||||||
|
local mapping = Null(new Mapping { ["a"] = 1 })
|
||||||
|
res1 = (mapping) {
|
||||||
|
[id("b")] = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { a = 1 })
|
||||||
|
res2 = (dynamic) {
|
||||||
|
[id("b")] = 2
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "a"; "b" })
|
||||||
|
res3 = (listing) {
|
||||||
|
[id(0)] = "updated a"
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Mapping { ["a"] = 1 })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
[id("b")] = 2
|
||||||
|
}
|
||||||
|
res4 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: listing entry with wrong key type
|
||||||
|
res5 = test.catch(() -> (Null(new Listing { "a" })) { [id("wrong type")] = "value" })
|
||||||
|
|
||||||
|
// error: listing entry index out of range
|
||||||
|
res6 = test.catch(() -> (Null(new Listing { "a" })) { [id(5)] = "value" })
|
||||||
|
|
||||||
|
// error: mapping cannot have a property (other than `default`)
|
||||||
|
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { foo = "bar"; [id("b")] = 2 })
|
||||||
|
|
||||||
|
// error: duplicate key
|
||||||
|
res8 = test.catch(() -> (Null(new Mapping {})) { [id("a")] = 1; [id("a")] = 2 })
|
||||||
|
|
||||||
|
// error: function amendment with wrong parameter count
|
||||||
|
local twoArgFun = (x, y) -> new Mapping {}
|
||||||
|
res9 = test.catch(() -> (Null(twoArgFun)) { x -> [id("a")] = 1 })
|
||||||
|
|
||||||
|
hidden foo: "foo"?
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res10 = test.catch(() -> (foo) { [id("a")] = 1 })
|
||||||
@@ -0,0 +1,68 @@
|
|||||||
|
// Covers GeneratorObjectLiteralNode's specializations for amending a `VmNull` whose default value
|
||||||
|
// is each of the underlying parent types it supports. Each body contains a `for`- or
|
||||||
|
// when-generator, which is what forces this node type instead of ElementsLiteralNode/
|
||||||
|
// EntriesLiteralNode/PropertiesLiteralNode.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
local class Person {
|
||||||
|
name: String
|
||||||
|
age: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { a = 1 })
|
||||||
|
res1 = (dynamic) {
|
||||||
|
for (i in List(2, 3)) {
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithTypedDefault
|
||||||
|
local person = Null(new Person { name = "Pigeon" })
|
||||||
|
res2 = (person) {
|
||||||
|
when (true) {
|
||||||
|
age = 10
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "a" })
|
||||||
|
res3 = (listing) {
|
||||||
|
for (i in List(2, 3)) {
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullableMapping
|
||||||
|
local mapping = Null(new Mapping { ["a"] = 1 })
|
||||||
|
res4 = (mapping) {
|
||||||
|
for (i in List(2, 3)) {
|
||||||
|
[i] = i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Dynamic { a = 1 })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
for (i in List(2, 3)) {
|
||||||
|
i
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res5 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: listing amendment cannot have parameters
|
||||||
|
res6 = test.catch(() -> (Null(new Listing { "a" })) { x -> for (i in List(2)) { i } })
|
||||||
|
|
||||||
|
// error: mapping amendment cannot have parameters
|
||||||
|
res7 = test.catch(() -> (Null(new Mapping { ["a"] = 1 })) { x -> for (i in List(2)) { [i] = i } })
|
||||||
|
|
||||||
|
// error: object amendment cannot have parameters
|
||||||
|
res8 = test.catch(() -> (Null(new Dynamic { a = 1 })) { x -> for (i in List(2)) { i } })
|
||||||
|
|
||||||
|
hidden foo: "foo"?
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res9 = test.catch(() -> (foo) { for (i in List(2)) { i } })
|
||||||
|
|
||||||
|
hidden x: List<Int>?
|
||||||
|
res10 = test.catch(() -> ((x) { when (true) { foo = 1 } }))
|
||||||
+63
@@ -0,0 +1,63 @@
|
|||||||
|
// Covers PropertiesLiteralNode's specializations for amending a `VmNull` whose default value is
|
||||||
|
// each of the underlying parent types it supports.
|
||||||
|
import "pkl:test"
|
||||||
|
|
||||||
|
local class Person {
|
||||||
|
name: String
|
||||||
|
age: Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithTypedDefault
|
||||||
|
local person = Null(new Person { name = "Pigeon" })
|
||||||
|
res1 = (person) {
|
||||||
|
age = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithDynamicDefault
|
||||||
|
local dynamic = Null(new Dynamic { name = "Pigeon" })
|
||||||
|
res2 = (dynamic) {
|
||||||
|
age = 3
|
||||||
|
}
|
||||||
|
|
||||||
|
// evalNullWithListingDefault
|
||||||
|
local listing = Null(new Listing { "one"; "two" })
|
||||||
|
res3 = (listing) {
|
||||||
|
default = (_) -> "three"
|
||||||
|
}.getOrDefault(5)
|
||||||
|
|
||||||
|
// evalNullableMapping
|
||||||
|
local mapping = Null(new Mapping { ["one"] = 1 })
|
||||||
|
res4 = (mapping) {
|
||||||
|
default = (_) -> 0
|
||||||
|
}.getOrDefault("missing")
|
||||||
|
|
||||||
|
// evalNullWithFunctionDefault
|
||||||
|
local fun = Null(() -> new Dynamic { zero = 0 })
|
||||||
|
local amendedFun = (fun) {
|
||||||
|
amended = "amended"
|
||||||
|
}
|
||||||
|
res5 = amendedFun.apply()
|
||||||
|
|
||||||
|
// error: typed object amendment with an unknown property
|
||||||
|
res6 = test.catch(() -> (Null(new Person { name = "Pigeon" })) { nickname = "Pidge" })
|
||||||
|
|
||||||
|
// error: listing cannot have a property (other than `default`)
|
||||||
|
res7 = test.catch(() -> (Null(new Listing { "one" })) { foo = "bar" })
|
||||||
|
|
||||||
|
// error: mapping cannot have a property (other than `default`)
|
||||||
|
res8 = test.catch(() -> (Null(new Mapping { ["one"] = 1 })) { foo = "bar" })
|
||||||
|
|
||||||
|
// error: function amendment with wrong parameter count
|
||||||
|
local twoArgFun = (x, y) -> new Dynamic {}
|
||||||
|
res9 = test.catch(() -> (Null(twoArgFun)) { x -> prop = x })
|
||||||
|
|
||||||
|
hidden foo: "foo"?
|
||||||
|
|
||||||
|
// error: default value's type has no matching specialization; falls back to the generic error
|
||||||
|
res10 = test.catch(() -> (foo) { foo = "bar" })
|
||||||
|
|
||||||
|
hidden clazz: Class?
|
||||||
|
|
||||||
|
res11 = (clazz) {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
+3
@@ -0,0 +1,3 @@
|
|||||||
|
res {
|
||||||
|
name = "Fred"
|
||||||
|
}
|
||||||
@@ -30,6 +30,37 @@ res2 {
|
|||||||
["two"] = 2
|
["two"] = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res2n {
|
||||||
|
null
|
||||||
|
true
|
||||||
|
42
|
||||||
|
1.23
|
||||||
|
"foo"
|
||||||
|
Regex("bar")
|
||||||
|
5.gb
|
||||||
|
3.min
|
||||||
|
Pair(1, 2)
|
||||||
|
List(1, 2, 3)
|
||||||
|
Set(1, 2, 3)
|
||||||
|
Map("one", 1, "two", 2)
|
||||||
|
new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
res3 {
|
res3 {
|
||||||
null
|
null
|
||||||
true
|
true
|
||||||
@@ -61,3 +92,34 @@ res3 {
|
|||||||
["two"] = 2
|
["two"] = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res3n {
|
||||||
|
null
|
||||||
|
true
|
||||||
|
42
|
||||||
|
1.23
|
||||||
|
"foo"
|
||||||
|
Regex("bar")
|
||||||
|
5.gb
|
||||||
|
3.min
|
||||||
|
Pair(1, 2)
|
||||||
|
List(1, 2, 3)
|
||||||
|
Set(1, 2, 3)
|
||||||
|
Map("one", 1, "two", 2)
|
||||||
|
new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
new {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -55,11 +55,20 @@ res7a = "Element index `-1` is out of range `0`..`2`."
|
|||||||
res8 = "Expected key of type `Int`, but got type `String`."
|
res8 = "Expected key of type `Int`, but got type `String`."
|
||||||
res8a = "Expected key of type `Int`, but got type `String`."
|
res8a = "Expected key of type `Int`, but got type `String`."
|
||||||
res9 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res9 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res9n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res10 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res10 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res10n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res11 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res11 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res11n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res12 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res12 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res12n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res12a = "Object of type `Listing` cannot have a property (other than `default`)."
|
res12a = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res12an = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res13 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res13 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res13n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res13a = "Object of type `Listing` cannot have a property (other than `default`)."
|
res13a = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res13an = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res14 = "Object of type `Listing` cannot have a property (other than `default`)."
|
res14 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res14n = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
res14a = "Object of type `Listing` cannot have a property (other than `default`)."
|
res14a = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res14an = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
|||||||
@@ -43,6 +43,50 @@ res2 {
|
|||||||
["two"] = 2
|
["two"] = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res2n {
|
||||||
|
[null] = null
|
||||||
|
[true] = true
|
||||||
|
[42] = 42
|
||||||
|
[1.23] = 1.23
|
||||||
|
["foo"] = "foo"
|
||||||
|
[Regex("bar")] = Regex("bar")
|
||||||
|
[5.gb] = 5.gb
|
||||||
|
[3.min] = 3.min
|
||||||
|
[Pair(1, 2)] = Pair(1, 2)
|
||||||
|
[List(1, 2, 3)] = List(1, 2, 3)
|
||||||
|
[Set(1, 2, 3)] = Set(1, 2, 3)
|
||||||
|
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
|
||||||
|
[new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}] {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}] {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}] {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}] {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
res3 {
|
res3 {
|
||||||
[null] = null
|
[null] = null
|
||||||
[true] = true
|
[true] = true
|
||||||
@@ -87,3 +131,55 @@ res3 {
|
|||||||
["two"] = 2
|
["two"] = 2
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
res3n {
|
||||||
|
[null] = null
|
||||||
|
[true] = true
|
||||||
|
[42] = 42
|
||||||
|
[1.23] = 1.23
|
||||||
|
["foo"] = "foo"
|
||||||
|
[Regex("bar")] = Regex("bar")
|
||||||
|
[5.gb] = 5.gb
|
||||||
|
[3.min] = 3.min
|
||||||
|
[Pair(1, 2)] = Pair(1, 2)
|
||||||
|
[List(1, 2, 3)] = List(1, 2, 3)
|
||||||
|
[Set(1, 2, 3)] = Set(1, 2, 3)
|
||||||
|
[Map("one", 1, "two", 2)] = Map("one", 1, "two", 2)
|
||||||
|
[new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}] {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}] {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 42
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}] {
|
||||||
|
1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
[new {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}] {
|
||||||
|
["one"] = 1
|
||||||
|
["two"] = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
res4 {
|
||||||
|
["foo"] = "1"
|
||||||
|
["bar"] = "2"
|
||||||
|
}
|
||||||
|
res4n {
|
||||||
|
["foo"] = "1"
|
||||||
|
["bar"] = "2"
|
||||||
|
}
|
||||||
|
|||||||
+21
@@ -0,0 +1,21 @@
|
|||||||
|
res1 {
|
||||||
|
["a"] = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
a = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res3 {
|
||||||
|
"updated a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res4 {
|
||||||
|
["a"] = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res5 = "Element index `5` is out of range `0`..`0`."
|
||||||
|
res6 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res7 = "Object of type `Mapping` cannot have a property (other than `default`)."
|
||||||
|
res8 = "Expected a function with 1 parameters, but got 2."
|
||||||
|
res9 = "Cannot instantiate, or amend an instance of, external class `String`."
|
||||||
@@ -0,0 +1,18 @@
|
|||||||
|
res1 {
|
||||||
|
a = 1
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
"a"
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
}
|
||||||
|
res3 {
|
||||||
|
"a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res4 = "Object of type `Mapping` cannot have an element."
|
||||||
|
res5 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res6 = "Expected a function with 1 parameters, but got 2."
|
||||||
|
res7 = "Expected value of type `Object | Function<Object>`, but got type `Int`. Value: 42"
|
||||||
+18
@@ -0,0 +1,18 @@
|
|||||||
|
res1 {
|
||||||
|
"updated a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
"updated a"
|
||||||
|
"b"
|
||||||
|
"c"
|
||||||
|
}
|
||||||
|
res3 {
|
||||||
|
"updated a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res4 = "Object of type `Mapping` cannot have an element."
|
||||||
|
res5 = "Element index `5` is out of range `0`..`0`."
|
||||||
|
res6 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res7 = "Expected a function with 1 parameters, but got 2."
|
||||||
|
res8 = "Expected value of type `Object | Function<Object>`, but got type `Int`. Value: 42"
|
||||||
@@ -0,0 +1,22 @@
|
|||||||
|
res1 {
|
||||||
|
["a"] = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
a = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res3 {
|
||||||
|
"updated a"
|
||||||
|
"b"
|
||||||
|
}
|
||||||
|
res4 {
|
||||||
|
["a"] = 1
|
||||||
|
["b"] = 2
|
||||||
|
}
|
||||||
|
res5 = "Expected key of type `Int`, but got type `String`."
|
||||||
|
res6 = "Element index `5` is out of range `0`..`0`."
|
||||||
|
res7 = "Object of type `Mapping` cannot have a property (other than `default`)."
|
||||||
|
res8 = "Duplicate definition of member `\"a\"`."
|
||||||
|
res9 = "Expected a function with 1 parameters, but got 2."
|
||||||
|
res10 = "Cannot instantiate, or amend an instance of, external class `String`."
|
||||||
+29
@@ -0,0 +1,29 @@
|
|||||||
|
res1 {
|
||||||
|
a = 1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 10
|
||||||
|
}
|
||||||
|
res3 {
|
||||||
|
"a"
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
res4 {
|
||||||
|
["a"] = 1
|
||||||
|
[2] = 2
|
||||||
|
[3] = 3
|
||||||
|
}
|
||||||
|
res5 {
|
||||||
|
a = 1
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
res6 = "Expected a function that returns a `Listing`, but got a `Listing`."
|
||||||
|
res7 = "Expected a function that returns a `Mapping`, but got a `Mapping`."
|
||||||
|
res8 = "Expected a function that returns an object, but got an object."
|
||||||
|
res9 = "Cannot instantiate, or amend an instance of, external class `String`."
|
||||||
|
res10 = "Cannot instantiate, or amend an instance of, external class `List`."
|
||||||
+22
@@ -0,0 +1,22 @@
|
|||||||
|
res1 {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 3
|
||||||
|
}
|
||||||
|
res2 {
|
||||||
|
name = "Pigeon"
|
||||||
|
age = 3
|
||||||
|
}
|
||||||
|
res3 = "three"
|
||||||
|
res4 = 0
|
||||||
|
res5 {
|
||||||
|
zero = 0
|
||||||
|
amended = "amended"
|
||||||
|
}
|
||||||
|
res6 = "Cannot find property `nickname` in object of type `properties#Person`."
|
||||||
|
res7 = "Object of type `Listing` cannot have a property (other than `default`)."
|
||||||
|
res8 = "Object of type `Mapping` cannot have a property (other than `default`)."
|
||||||
|
res9 = "Expected a function with 1 parameters, but got 2."
|
||||||
|
res10 = "Cannot instantiate, or amend an instance of, external class `String`."
|
||||||
|
res11 {
|
||||||
|
x = 1
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user