diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java index b80ccc4c..3b34d2c1 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java @@ -1986,6 +1986,7 @@ public final class AstBuilder extends AbstractAstBuilder { visitArgumentList(argCtx), MemberLookupMode.EXPLICIT_RECEIVER, needsConst, + symbolTable.getCurrentScope().isVisitingIterable(), PropagateNullReceiverNodeGen.create(unavailableSourceSection(), receiver), GetClassNodeGen.create(null))); } @@ -1998,6 +1999,7 @@ public final class AstBuilder extends AbstractAstBuilder { visitArgumentList(argCtx), MemberLookupMode.EXPLICIT_RECEIVER, needsConst, + symbolTable.getCurrentScope().isVisitingIterable(), receiver, GetClassNodeGen.create(null)); } @@ -2072,7 +2074,11 @@ public final class AstBuilder extends AbstractAstBuilder { } return InvokeSuperMethodNodeGen.create( - sourceSection, memberName, visitArgumentList(argCtx), needsConst); + sourceSection, + memberName, + symbolTable.getCurrentScope().isVisitingIterable(), + visitArgumentList(argCtx), + needsConst); } // superproperty call @@ -2130,7 +2136,8 @@ public final class AstBuilder extends AbstractAstBuilder { isBaseModule, scope.isCustomThisScope(), scope.getConstLevel(), - scope.getConstDepth()); + scope.getConstDepth(), + scope.isVisitingIterable()); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java index 02cfbe75..6a3a63f6 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java @@ -71,6 +71,6 @@ public final class LetExprNode extends ExpressionNode { var value = valueNode.executeGeneric(frame); - return callNode.call(function.getThisValue(), function, value); + return callNode.call(function.getThisValue(), function, false, value); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java index df4b155e..337c777b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java @@ -184,7 +184,8 @@ public final class AmendFunctionNode extends PklNode { var arguments = new Object[frameArguments.length]; arguments[0] = functionToAmend.getThisValue(); arguments[1] = functionToAmend; - System.arraycopy(frameArguments, 2, arguments, 2, frameArguments.length - 2); + arguments[2] = false; + System.arraycopy(frameArguments, 3, arguments, 3, frameArguments.length - 3); var valueToAmend = callNode.call(functionToAmend.getCallTarget(), arguments); if (!(valueToAmend instanceof VmFunction newFunctionToAmend)) { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java index 517c98e4..33fed39b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java @@ -28,6 +28,7 @@ public final class InvokeMethodDirectNode extends ExpressionNode { private final VmObjectLike owner; @Child private ExpressionNode receiverNode; @Children private final ExpressionNode[] argumentNodes; + private final boolean isInIterable; @Child private DirectCallNode callNode; @@ -35,12 +36,14 @@ public final class InvokeMethodDirectNode extends ExpressionNode { SourceSection sourceSection, ClassMethod method, ExpressionNode receiverNode, - ExpressionNode[] argumentNodes) { + ExpressionNode[] argumentNodes, + boolean isInIterable) { super(sourceSection); this.owner = method.getOwner(); this.receiverNode = receiverNode; this.argumentNodes = argumentNodes; + this.isInIterable = isInIterable; callNode = DirectCallNode.create(method.getCallTarget(sourceSection)); } @@ -48,11 +51,12 @@ public final class InvokeMethodDirectNode extends ExpressionNode { @Override @ExplodeLoop public Object executeGeneric(VirtualFrame frame) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = receiverNode.executeGeneric(frame); args[1] = owner; + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(args); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java index 37c5fc29..7babf917 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java @@ -33,29 +33,33 @@ public final class InvokeMethodLexicalNode extends ExpressionNode { private final int levelsUp; @Child private DirectCallNode callNode; + private final boolean isInIterable; InvokeMethodLexicalNode( SourceSection sourceSection, CallTarget callTarget, int levelsUp, - ExpressionNode[] argumentNodes) { + ExpressionNode[] argumentNodes, + boolean isInIterable) { super(sourceSection); this.levelsUp = levelsUp; this.argumentNodes = argumentNodes; callNode = DirectCallNode.create(callTarget); + this.isInIterable = isInIterable; } @Override @ExplodeLoop public Object executeGeneric(VirtualFrame frame) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; var enclosingFrame = getEnclosingFrame(frame); args[0] = VmUtils.getReceiver(enclosingFrame); args[1] = VmUtils.getOwner(enclosingFrame); + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(args); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java index bb8c0400..dd263a04 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java @@ -36,6 +36,7 @@ import org.pkl.core.runtime.VmClass; import org.pkl.core.runtime.VmFunction; /** A virtual method call. */ +@SuppressWarnings("DuplicatedCode") @ImportStatic(Identifier.class) @NodeChild(value = "receiverNode", type = ExpressionNode.class) @NodeChild(value = "receiverClassNode", type = GetClassNode.class, executeWith = "receiverNode") @@ -44,27 +45,31 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode { @Children private final ExpressionNode[] argumentNodes; private final MemberLookupMode lookupMode; private final boolean needsConst; + private final boolean isInIterable; protected InvokeMethodVirtualNode( SourceSection sourceSection, Identifier methodName, ExpressionNode[] argumentNodes, MemberLookupMode lookupMode, - boolean needsConst) { + boolean needsConst, + boolean isInIterable) { super(sourceSection); this.methodName = methodName; this.argumentNodes = argumentNodes; this.lookupMode = lookupMode; this.needsConst = needsConst; + this.isInIterable = isInIterable; } protected InvokeMethodVirtualNode( SourceSection sourceSection, Identifier methodName, ExpressionNode[] argumentNodes, - MemberLookupMode lookupMode) { - this(sourceSection, methodName, argumentNodes, lookupMode, false); + MemberLookupMode lookupMode, + boolean isInIterable) { + this(sourceSection, methodName, argumentNodes, lookupMode, false, isInIterable); } /** @@ -84,11 +89,12 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = receiver.getThisValue(); args[1] = receiver; + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(args); @@ -103,11 +109,12 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode { @SuppressWarnings("unused") VmClass receiverClass, @Exclusive @Cached("create()") IndirectCallNode callNode) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = receiver.getThisValue(); args[1] = receiver; + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(receiver.getCallTarget(), args); @@ -123,11 +130,12 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode { @Cached("resolveMethod(receiverClass)") ClassMethod method, @Cached("create(method.getCallTarget(sourceSection))") DirectCallNode callNode) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = receiver; args[1] = method.getOwner(); + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(args); @@ -142,11 +150,12 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode { @Exclusive @Cached("create()") IndirectCallNode callNode) { var method = resolveMethod(receiverClass); - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = receiver; args[1] = method.getOwner(); + args[2] = isInIterable; for (var i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } // Deprecation should not report here (getCallTarget(sourceSection)), as this happens for each diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java index e70e2fb8..700f13d1 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java @@ -30,15 +30,18 @@ import org.pkl.core.runtime.VmUtils; public abstract class InvokeSuperMethodNode extends ExpressionNode { private final Identifier methodName; @Children private final ExpressionNode[] argumentNodes; + private final boolean isInIterable; private final boolean needsConst; protected InvokeSuperMethodNode( SourceSection sourceSection, Identifier methodName, + boolean isInIterable, ExpressionNode[] argumentNodes, boolean needsConst) { super(sourceSection); + this.isInIterable = isInIterable; this.needsConst = needsConst; assert !methodName.isLocalMethod(); @@ -54,11 +57,12 @@ public abstract class InvokeSuperMethodNode extends ExpressionNode { @Cached(value = "findSupermethod(frame)", neverDefault = true) ClassMethod supermethod, @Cached("create(supermethod.getCallTarget(sourceSection))") DirectCallNode callNode) { - var args = new Object[2 + argumentNodes.length]; + var args = new Object[3 + argumentNodes.length]; args[0] = VmUtils.getReceiverOrNull(frame); args[1] = supermethod.getOwner(); + args[2] = isInIterable; for (int i = 0; i < argumentNodes.length; i++) { - args[2 + i] = argumentNodes[i].executeGeneric(frame); + args[3 + i] = argumentNodes[i].executeGeneric(frame); } return callNode.call(args); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java index d2d0b551..73735481 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java @@ -50,6 +50,7 @@ public final class ResolveMethodNode extends ExpressionNode { private final boolean isCustomThisScope; private final ConstLevel constLevel; private final int constDepth; + private final boolean isInIterable; public ResolveMethodNode( SourceSection sourceSection, @@ -58,7 +59,8 @@ public final class ResolveMethodNode extends ExpressionNode { boolean isBaseModule, boolean isCustomThisScope, ConstLevel constLevel, - int constDepth) { + int constDepth, + boolean isInIterable) { super(sourceSection); @@ -68,6 +70,7 @@ public final class ResolveMethodNode extends ExpressionNode { this.isCustomThisScope = isCustomThisScope; this.constLevel = constLevel; this.constDepth = constDepth; + this.isInIterable = isInIterable; } @Override @@ -91,7 +94,11 @@ public final class ResolveMethodNode extends ExpressionNode { assert localMethod.isLocal(); checkConst(currOwner, localMethod, levelsUp); return new InvokeMethodLexicalNode( - sourceSection, localMethod.getCallTarget(sourceSection), levelsUp, argumentNodes); + sourceSection, + localMethod.getCallTarget(sourceSection), + levelsUp, + argumentNodes, + isInIterable); } var method = currOwner.getVmClass().getDeclaredMethod(methodName); if (method != null) { @@ -99,7 +106,11 @@ public final class ResolveMethodNode extends ExpressionNode { checkConst(currOwner, method, levelsUp); if (method.getDeclaringClass().isClosed()) { return new InvokeMethodLexicalNode( - sourceSection, method.getCallTarget(sourceSection), levelsUp, argumentNodes); + sourceSection, + method.getCallTarget(sourceSection), + levelsUp, + argumentNodes, + isInIterable); } //noinspection ConstantConditions @@ -108,6 +119,7 @@ public final class ResolveMethodNode extends ExpressionNode { methodName, argumentNodes, MemberLookupMode.IMPLICIT_LEXICAL, + isInIterable, levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp), GetClassNodeGen.create(null)); } @@ -122,7 +134,7 @@ public final class ResolveMethodNode extends ExpressionNode { (CallTarget) localMethod.getCallTarget().call(currOwner, currOwner); return new InvokeMethodLexicalNode( - sourceSection, methodCallTarget, levelsUp, argumentNodes); + sourceSection, methodCallTarget, levelsUp, argumentNodes, isInIterable); } } @@ -138,7 +150,7 @@ public final class ResolveMethodNode extends ExpressionNode { if (method != null) { assert !method.isLocal(); return new InvokeMethodDirectNode( - sourceSection, method, new ConstantValueNode(baseModule), argumentNodes); + sourceSection, method, new ConstantValueNode(baseModule), argumentNodes, isInIterable); } } @@ -158,6 +170,7 @@ public final class ResolveMethodNode extends ExpressionNode { argumentNodes, MemberLookupMode.IMPLICIT_THIS, needsConst, + isInIterable, VmUtils.createThisNode(VmUtils.unavailableSourceSection(), isCustomThisScope), GetClassNodeGen.create(null)); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java index 77303bc3..7e8a92d1 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java @@ -80,6 +80,7 @@ public abstract class ToStringNode extends UnaryExpressionNode { Identifier.TO_STRING, new ExpressionNode[] {}, MemberLookupMode.EXPLICIT_RECEIVER, + false, null, null); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java index d10f6752..5c30bcb3 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java @@ -33,12 +33,12 @@ public abstract class ApplyVmFunction0Node extends PklNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function); + return callNode.call(function.getThisValue(), function, false); } @Specialization(replaces = "evalDirect") protected Object eval(VmFunction function, @Cached("create()") IndirectCallNode callNode) { - return callNode.call(function.getCallTarget(), function.getThisValue(), function); + return callNode.call(function.getCallTarget(), function.getThisValue(), function, false); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java index 063b7133..b44eca03 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java @@ -77,13 +77,13 @@ public abstract class ApplyVmFunction1Node extends ExpressionNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function, arg1); + return callNode.call(function.getThisValue(), function, false, arg1); } @Specialization(replaces = "evalDirect") protected Object eval( VmFunction function, Object arg1, @Cached("create()") IndirectCallNode callNode) { - return callNode.call(function.getCallTarget(), function.getThisValue(), function, arg1); + return callNode.call(function.getCallTarget(), function.getThisValue(), function, false, arg1); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java index 1f3acb22..b8c9fb38 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java @@ -76,7 +76,7 @@ public abstract class ApplyVmFunction2Node extends PklNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function, arg1, arg2); + return callNode.call(function.getThisValue(), function, false, arg1, arg2); } @Specialization(replaces = "evalDirect") @@ -86,6 +86,7 @@ public abstract class ApplyVmFunction2Node extends PklNode { Object arg2, @Cached("create()") IndirectCallNode callNode) { - return callNode.call(function.getCallTarget(), function.getThisValue(), function, arg1, arg2); + return callNode.call( + function.getCallTarget(), function.getThisValue(), function, false, arg1, arg2); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java index 2e41ed1f..c88c6687 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java @@ -36,7 +36,7 @@ public abstract class ApplyVmFunction3Node extends PklNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function, arg1, arg2, arg3); + return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3); } @Specialization(replaces = "evalDirect") @@ -48,6 +48,6 @@ public abstract class ApplyVmFunction3Node extends PklNode { @Cached("create()") IndirectCallNode callNode) { return callNode.call( - function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3); + function.getCallTarget(), function.getThisValue(), function, false, arg1, arg2, arg3); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java index 6012558b..b716f2ac 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java @@ -38,7 +38,7 @@ public abstract class ApplyVmFunction4Node extends PklNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function, arg1, arg2, arg3, arg4); + return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3, arg4); } @Specialization(replaces = "evalDirect") @@ -51,6 +51,6 @@ public abstract class ApplyVmFunction4Node extends PklNode { @Cached("create()") IndirectCallNode callNode) { return callNode.call( - function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3, arg4); + function.getCallTarget(), function.getThisValue(), function, false, arg1, arg2, arg3, arg4); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java index affa9b47..8f699838 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java @@ -39,7 +39,7 @@ public abstract class ApplyVmFunction5Node extends PklNode { RootCallTarget cachedCallTarget, @Cached("create(cachedCallTarget)") DirectCallNode callNode) { - return callNode.call(function.getThisValue(), function, arg1, arg2, arg3, arg4, arg5); + return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3, arg4, arg5); } @Specialization(replaces = "evalDirect") @@ -53,6 +53,14 @@ public abstract class ApplyVmFunction5Node extends PklNode { @Cached("create()") IndirectCallNode callNode) { return callNode.call( - function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3, arg4, arg5); + function.getCallTarget(), + function.getThisValue(), + function, + false, + arg1, + arg2, + arg3, + arg4, + arg5); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java index 284e8c07..8adbbefc 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java @@ -44,7 +44,11 @@ public final class FunctionNode extends RegularMemberNode { // For VmObject receivers, the owner is the same as or an ancestor of the receiver. // For other receivers, the owner is the prototype of the receiver's class. // The chain of enclosing owners forms a function/property's lexical scope. - private static final int IMPLICIT_PARAM_COUNT = 2; + // + // For function calls only, a third implicit argument is passed; whether the call came from within + // an iterable node or not. + // This is a mitigation for an existing bug (https://github.com/apple/pkl/issues/741). + private static final int IMPLICIT_PARAM_COUNT = 3; private final int paramCount; private final int totalParamCount; @@ -109,10 +113,15 @@ public final class FunctionNode extends RegularMemberNode { throw wrongArgumentCount(totalArgCount - IMPLICIT_PARAM_COUNT); } + var isInIterable = (boolean) frame.getArguments()[2]; try { for (var i = 0; i < parameterTypeNodes.length; i++) { var argument = frame.getArguments()[IMPLICIT_PARAM_COUNT + i]; - parameterTypeNodes[i].executeAndSet(frame, argument); + if (isInIterable) { + parameterTypeNodes[i].executeEagerlyAndSet(frame, argument); + } else { + parameterTypeNodes[i].executeAndSet(frame, argument); + } } var result = bodyNode.executeGeneric(frame); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java index a0256e5a..652b9dcf 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java @@ -55,16 +55,16 @@ public final class IdentityMixinNode extends PklRootNode { @Override public Object execute(VirtualFrame frame) { var arguments = frame.getArguments(); - if (arguments.length != 3) { + if (arguments.length != 4) { CompilerDirectives.transferToInterpreter(); throw exceptionBuilder() - .evalError("wrongFunctionArgumentCount", 1, arguments.length - 2) + .evalError("wrongFunctionArgumentCount", 1, arguments.length - 3) .withSourceSection(sourceSection) .build(); } try { - var argument = arguments[2]; + var argument = arguments[3]; if (argumentTypeNode != null) { return argumentTypeNode.execute(frame, argument); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 8c8dce1e..149b4367 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -94,6 +94,14 @@ public abstract class TypeNode extends PklNode { return execute(frame, value); } + /** + * Checks if {@code value} conforms to this type. + * + *

If {@code value} is conforming, sets {@code slot} to {@code value}. Otherwise, throws a + * {@link VmTypeMismatchException}. + */ + public abstract Object executeEagerlyAndSet(VirtualFrame frame, Object value); + // method arguments are used when default value contains a root node public @Nullable Object createDefaultValue( VmLanguage language, @@ -213,6 +221,11 @@ public abstract class TypeNode extends PklNode { frame.setLong(slot, (long) value); return value; } + + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + return executeAndSet(frame, value); + } } public abstract static class ObjectSlotTypeNode extends FrameSlotTypeNode { @@ -230,6 +243,13 @@ public abstract class TypeNode extends PklNode { frame.setObject(slot, result); return result; } + + @Override + public final Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + var result = executeEagerly(frame, value); + frame.setObject(slot, result); + return result; + } } /** @@ -263,6 +283,13 @@ public abstract class TypeNode extends PklNode { writeSlotNode.executeWithValue(frame, result); return result; } + + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + var result = executeEagerly(frame, value); + writeSlotNode.executeWithValue(frame, result); + return result; + } } /** The `unknown` type. */ @@ -328,6 +355,11 @@ public abstract class TypeNode extends PklNode { throw PklBugException.unreachableCode(); } + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + return executeAndSet(frame, value); + } + @Override public FrameSlotKind getFrameSlotKind() { return FrameSlotKind.Illegal; @@ -2382,6 +2414,22 @@ public abstract class TypeNode extends PklNode { } } + /** See docstring on {@link TypeAliasTypeNode#execute}. */ + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + var prevOwner = VmUtils.getOwner(frame); + var prevReceiver = VmUtils.getReceiver(frame); + VmUtils.setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame())); + VmUtils.setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame())); + + try { + return aliasedTypeNode.executeEagerlyAndSet(frame, value); + } finally { + VmUtils.setOwner(frame, prevOwner); + VmUtils.setReceiver(frame, prevReceiver); + } + } + @Override @TruffleBoundary public @Nullable Object createDefaultValue( @@ -2501,6 +2549,13 @@ public abstract class TypeNode extends PklNode { return ret; } + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + var ret = executeEagerly(frame, value); + childNode.executeEagerlyAndSet(frame, ret); + return ret; + } + @Override public @Nullable Object createDefaultValue( VmLanguage language, SourceSection headerSection, String qualifiedName) { @@ -2648,6 +2703,11 @@ public abstract class TypeNode extends PklNode { } } + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + return executeAndSet(frame, value); + } + @Override public VmClass getVmClass() { return BaseModule.getNumberClass(); @@ -2716,6 +2776,11 @@ public abstract class TypeNode extends PklNode { return value; } + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + return executeAndSet(frame, value); + } + @Override public VmClass getVmClass() { return BaseModule.getFloatClass(); @@ -2756,6 +2821,11 @@ public abstract class TypeNode extends PklNode { return value; } + @Override + public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { + return executeAndSet(frame, value); + } + @Override public VmClass getVmClass() { return BaseModule.getBooleanClass(); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java index cedfe9d8..e9cb3f66 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java @@ -55,7 +55,7 @@ public final class VmFunction extends VmObjectLike { // if call site is a node, use ApplyVmFunction1Node.execute() or DirectCallNode.call() instead of // this method public Object apply(Object arg1) { - return getCallTarget().call(thisValue, this, arg1); + return getCallTarget().call(thisValue, this, false, arg1); } public String applyString(Object arg1) { @@ -69,7 +69,7 @@ public final class VmFunction extends VmObjectLike { // if call site is a node, use ApplyVmFunction2Node.execute() or DirectCallNode.call() instead of // this method public Object apply(Object arg1, Object arg2) { - return getCallTarget().call(thisValue, this, arg1, arg2); + return getCallTarget().call(thisValue, this, false, arg1, arg2); } public VmFunction copy( diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java index 53426949..1faf2aa5 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java @@ -31,11 +31,12 @@ public final class FunctionNodes { protected Object eval(VmFunction self, VmList argList) { var argCount = argList.getLength(); - var args = new Object[2 + argCount]; + var args = new Object[3 + argCount]; args[0] = self.getThisValue(); args[1] = self; + args[2] = false; - var i = 2; + var i = 3; for (var arg : argList) { args[i++] = arg; } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference.pkl index dda47f76..77fd6ff7 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference.pkl +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference.pkl @@ -31,3 +31,27 @@ res2 { }.birds } } + +res3 { + for (key, _ in Map("hello-there", 5)) { + ...myself(new Listing { + new Listing { + key + } + }) + } +} + +res4 { + for (key, _ in Map("hello-there", 5)) { + ...myself2.apply(new Listing { + new Listing { + key + } + }) + } +} + +function myself(l: Listing>) = l + +local myself2 = (l: Listing>) -> l diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference.pcf index 18c86a66..a6554329 100644 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference.pcf +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference.pcf @@ -8,3 +8,13 @@ res2 { age = 1 } } +res3 { + new { + "hello-there" + } +} +res4 { + new { + "hello-there" + } +}