mirror of
https://github.com/apple/pkl.git
synced 2026-09-06 01:47:31 +02:00
Add inference for implicitly typed new in method arguments (#1768)
This commit is contained in:
@@ -109,8 +109,9 @@ import org.pkl.core.ast.expression.literal.MapLiteralNode;
|
||||
import org.pkl.core.ast.expression.literal.PropertiesLiteralNodeGen;
|
||||
import org.pkl.core.ast.expression.literal.SetLiteralNode;
|
||||
import org.pkl.core.ast.expression.literal.TrueLiteralNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinMethodNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNode;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinMethodArgumentNodeGen;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinMethodNodeGen;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinObjectMethodNodeGen;
|
||||
import org.pkl.core.ast.expression.member.InferParentWithinPropertyNodeGen;
|
||||
import org.pkl.core.ast.expression.member.InvokeLexicalClassMethodNode;
|
||||
import org.pkl.core.ast.expression.member.InvokeLexicalObjectMethodNode;
|
||||
@@ -168,7 +169,7 @@ import org.pkl.core.ast.member.UnresolvedFunctionNode;
|
||||
import org.pkl.core.ast.member.UnresolvedMethodNode;
|
||||
import org.pkl.core.ast.member.UnresolvedPropertyNode;
|
||||
import org.pkl.core.ast.member.UntypedObjectMemberNode;
|
||||
import org.pkl.core.ast.type.GetParentForTypeNode;
|
||||
import org.pkl.core.ast.type.GetParentForTypeNodeGen;
|
||||
import org.pkl.core.ast.type.ResolveDeclaredTypeNode;
|
||||
import org.pkl.core.ast.type.ResolveQualifiedDeclaredTypeNode;
|
||||
import org.pkl.core.ast.type.ResolveSimpleDeclaredTypeNode;
|
||||
@@ -1034,8 +1035,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var expr =
|
||||
doVisitObjectBody(
|
||||
newExpr.getBody(),
|
||||
new GetParentForTypeNode(
|
||||
GetParentForTypeNodeGen.create(
|
||||
createSourceSection(newExpr),
|
||||
language,
|
||||
parentType,
|
||||
symbolTable.getCurrentScope().getQualifiedName()));
|
||||
if (type instanceof DeclaredType declaredType && declaredType.getArgs() != null) {
|
||||
@@ -1079,9 +1081,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
org.pkl.core.runtime.Identifier scopeName = scope.getName();
|
||||
inferredParentNode =
|
||||
isObjectMethod
|
||||
? new InferParentWithinObjectMethodNode(
|
||||
? InferParentWithinObjectMethodNodeGen.create(
|
||||
createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode())
|
||||
: new InferParentWithinMethodNode(
|
||||
: InferParentWithinMethodNodeGen.create(
|
||||
createSourceSection(expr.newSpan()), language, scopeName, new GetOwnerNode());
|
||||
} else if (parent instanceof LetExpr letExpr && letExpr.getBindingExpr() == child) {
|
||||
// TODO correctly infer parent, e.g. `let (x: Person = new {}) ...`
|
||||
@@ -1089,6 +1091,16 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
.evalError("cannotInferParent")
|
||||
.withSourceSection(createSourceSection(expr.newSpan()))
|
||||
.build();
|
||||
} else if (parent instanceof ArgumentList argumentList) {
|
||||
// cases we can't cover currently
|
||||
// - FunctionN.apply: the parameter type nodes are not stored
|
||||
// - pkl.base intrinsic constructors: List(), Set(), Map(), Bytes()
|
||||
// - generic methods: pkl.base#Pair(), etc.
|
||||
// these will throw cannotInferParent at runtime
|
||||
var sourceSection = createSourceSection(expr.newSpan());
|
||||
var argIndex = argumentList.getArguments().indexOf(child);
|
||||
inferredParentNode =
|
||||
InferParentWithinMethodArgumentNodeGen.create(sourceSection, language, argIndex);
|
||||
} else {
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotInferParent")
|
||||
|
||||
@@ -40,6 +40,7 @@ import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
import org.pkl.core.runtime.VmDuration;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.ArrayUtils;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.parser.Lexer;
|
||||
@@ -1066,16 +1067,9 @@ public final class SymbolTable {
|
||||
* A scope where {@code this} has a special meaning (type constraint, object member predicate).
|
||||
*
|
||||
* <p>Technically, a scope where {@code this} isn't {@code frame.getArguments()[0]}, but the value
|
||||
* at an auxiliary slot identified by {@link CustomThisScope#FRAME_SLOT_ID}.
|
||||
* at an auxiliary slot identified by {@link VmUtils#CUSTOM_THIS_FRAME_SLOT_ID}.
|
||||
*/
|
||||
public static final class CustomThisScope extends Scope {
|
||||
public static final Object FRAME_SLOT_ID =
|
||||
new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "customThisSlot";
|
||||
}
|
||||
};
|
||||
|
||||
public CustomThisScope(Scope parent, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(
|
||||
|
||||
+2
-3
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
@@ -22,7 +22,6 @@ import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.UnexpectedResultException;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
@@ -132,7 +131,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
|
||||
customThisSlot =
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+41
@@ -0,0 +1,41 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.dsl.Idempotent;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
|
||||
public abstract class AbstractInferParentFromMethodNode extends AbstractInferParentNode {
|
||||
|
||||
public AbstractInferParentFromMethodNode(SourceSection sourceSection, VmLanguage language) {
|
||||
super(sourceSection, language);
|
||||
}
|
||||
|
||||
protected abstract Method getMethod(VirtualFrame frame);
|
||||
|
||||
protected abstract @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method);
|
||||
|
||||
@Idempotent
|
||||
protected boolean isFinalType(Method method, @Nullable TypeNode typeNode) {
|
||||
return method instanceof ObjectMethodNode || (typeNode != null && typeNode.isFinalType());
|
||||
}
|
||||
}
|
||||
+69
@@ -0,0 +1,69 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode.TypeVariableNode;
|
||||
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
|
||||
import org.pkl.core.runtime.VmDynamic;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class AbstractInferParentNode extends ExpressionNode {
|
||||
|
||||
protected final VmLanguage language;
|
||||
|
||||
public AbstractInferParentNode(SourceSection sourceSection, VmLanguage language) {
|
||||
super(sourceSection);
|
||||
this.language = language;
|
||||
}
|
||||
|
||||
protected final Object getDefaultValue(
|
||||
VirtualFrame frame,
|
||||
@Nullable TypeNode typeNode,
|
||||
SourceSection headerSection,
|
||||
String qualifiedName) {
|
||||
if (typeNode == null || typeNode instanceof UnknownTypeNode) {
|
||||
return VmDynamic.empty();
|
||||
}
|
||||
|
||||
var defaultValue = typeNode.createDefaultValue(frame, language, headerSection, qualifiedName);
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
if (typeNode instanceof TypeVariableNode) {
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
|
||||
// try to produce a more specific error message than "cannotInstantiateType"
|
||||
var clazz = typeNode.getVmClass();
|
||||
if (clazz != null) {
|
||||
VmUtils.checkIsInstantiable(clazz, typeNode);
|
||||
}
|
||||
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotInstantiateType", typeNode.getSourceSection().getCharacters())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
+2
-1
@@ -21,7 +21,8 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract sealed class AbstractInvokeLexicalMethodNode extends AbstractInvokeMethodNode
|
||||
public abstract sealed class AbstractInvokeLexicalMethodNode
|
||||
extends AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
permits InvokeLexicalClassMethodNode, InvokeLexicalObjectMethodNode {
|
||||
private final int levelsUp;
|
||||
|
||||
|
||||
+86
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
/**
|
||||
* A non-virtual (statically dispatched) method call.
|
||||
*
|
||||
* <p>Subclasses differ only in how they obtain the {@code owner}/{@code receiver} that the method
|
||||
* is invoked on: either by walking the frame chain ({@link InvokeLexicalClassMethodNode}, {@link
|
||||
* InvokeLexicalObjectMethodNode}), or off of an explicit receiver expression ({@link
|
||||
* InvokeQualifiedClassMethodNode}, {@link InvokeQualifiedObjectMethodNode}).
|
||||
*/
|
||||
public abstract sealed class AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
extends AbstractInvokeMethodNode
|
||||
permits AbstractInvokeQualifiedMethodNode, AbstractInvokeLexicalMethodNode {
|
||||
|
||||
protected final Identifier methodName;
|
||||
private final boolean needsConst;
|
||||
@Child private @Nullable DirectCallNode callNode;
|
||||
@CompilationFinal protected boolean isConstChecked;
|
||||
|
||||
protected AbstractInvokeLexicalOrQualifiedMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst) {
|
||||
super(sourceSection, argumentNodes);
|
||||
this.methodName = methodName;
|
||||
this.needsConst = needsConst;
|
||||
this.isConstChecked = false;
|
||||
}
|
||||
|
||||
protected final Object invoke(VirtualFrame frame, VmObjectLike owner, Object receiver) {
|
||||
checkConst(owner);
|
||||
var method = getMethod(owner);
|
||||
var args = evalArgs(frame, method, owner, receiver);
|
||||
return getCallNode(method, owner).call(args);
|
||||
}
|
||||
|
||||
private void checkConst(VmObjectLike owner) {
|
||||
if (!needsConst || isConstChecked) {
|
||||
return;
|
||||
}
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
doCheckConst(owner);
|
||||
isConstChecked = true;
|
||||
}
|
||||
|
||||
protected abstract Method getMethod(VmObjectLike owner);
|
||||
|
||||
protected abstract void doCheckConst(VmObjectLike owner);
|
||||
|
||||
protected DirectCallNode getCallNode(Method method, VmObjectLike owner) {
|
||||
if (callNode == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
callNode = DirectCallNode.create(method.getCallTarget(getSourceSection(), owner));
|
||||
insert(callNode);
|
||||
}
|
||||
assert callNode != null;
|
||||
return callNode;
|
||||
}
|
||||
}
|
||||
+29
-56
@@ -15,79 +15,52 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
/**
|
||||
* A non-virtual (statically dispatched) method call.
|
||||
*
|
||||
* <p>Subclasses differ only in how they obtain the {@code owner}/{@code receiver} that the method
|
||||
* is invoked on: either by walking the frame chain ({@link InvokeLexicalClassMethodNode}, {@link
|
||||
* InvokeLexicalObjectMethodNode}), or off of an explicit receiver expression ({@link
|
||||
* InvokeQualifiedClassMethodNode}, {@link InvokeQualifiedObjectMethodNode}).
|
||||
*/
|
||||
public abstract sealed class AbstractInvokeMethodNode extends ExpressionNode
|
||||
permits AbstractInvokeQualifiedMethodNode, AbstractInvokeLexicalMethodNode {
|
||||
public abstract class AbstractInvokeMethodNode extends ExpressionNode {
|
||||
|
||||
protected final Identifier methodName;
|
||||
private final boolean needsConst;
|
||||
@Children private ExpressionNode[] argumentNodes;
|
||||
@Child private @Nullable DirectCallNode callNode;
|
||||
@CompilationFinal protected boolean isConstChecked;
|
||||
@Children protected final ExpressionNode[] argumentNodes;
|
||||
|
||||
protected AbstractInvokeMethodNode(
|
||||
SourceSection sourceSection,
|
||||
Identifier methodName,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst) {
|
||||
public AbstractInvokeMethodNode(SourceSection sourceSection, ExpressionNode[] argumentNodes) {
|
||||
super(sourceSection);
|
||||
this.methodName = methodName;
|
||||
this.argumentNodes = argumentNodes;
|
||||
this.needsConst = needsConst;
|
||||
this.isConstChecked = false;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private int getMethodSlot(FrameDescriptor frameDescriptor) {
|
||||
// can't store the slot id as this node may be called from different root nodes
|
||||
// (see constraints14 snippet)
|
||||
return frameDescriptor.findOrAddAuxiliarySlot(VmUtils.METHOD_FRAME_SLOT_ID);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
protected final Object invoke(VirtualFrame frame, VmObjectLike owner, Object receiver) {
|
||||
checkConst(owner);
|
||||
protected Object[] evalArgs(
|
||||
VirtualFrame frame, @Nullable Method method, Object owner, @Nullable Object receiver) {
|
||||
// TODO: optimize this away when the call does not contain any implicit new args
|
||||
var methodSlot = getMethodSlot(frame.getFrameDescriptor());
|
||||
var prevMethod = frame.getAuxiliarySlot(methodSlot);
|
||||
frame.setAuxiliarySlot(methodSlot, method);
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver;
|
||||
args[1] = owner;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
|
||||
try {
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
} finally {
|
||||
frame.setAuxiliarySlot(methodSlot, prevMethod);
|
||||
}
|
||||
return getCallNode(owner).call(args);
|
||||
}
|
||||
|
||||
private void checkConst(VmObjectLike owner) {
|
||||
if (!needsConst || isConstChecked) {
|
||||
return;
|
||||
}
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
doCheckConst(owner);
|
||||
isConstChecked = true;
|
||||
}
|
||||
|
||||
protected abstract CallTarget getCallTarget(VmObjectLike owner);
|
||||
|
||||
protected abstract void doCheckConst(VmObjectLike owner);
|
||||
|
||||
protected DirectCallNode getCallNode(VmObjectLike owner) {
|
||||
if (callNode == null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
callNode = DirectCallNode.create(getCallTarget(owner));
|
||||
insert(callNode);
|
||||
}
|
||||
assert callNode != null;
|
||||
return callNode;
|
||||
return args;
|
||||
}
|
||||
}
|
||||
|
||||
+2
-1
@@ -21,7 +21,8 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
|
||||
public abstract sealed class AbstractInvokeQualifiedMethodNode extends AbstractInvokeMethodNode
|
||||
public abstract sealed class AbstractInvokeQualifiedMethodNode
|
||||
extends AbstractInvokeLexicalOrQualifiedMethodNode
|
||||
permits InvokeQualifiedClassMethodNode, InvokeQualifiedObjectMethodNode {
|
||||
@Child private ExpressionNode getReceiverNode;
|
||||
|
||||
|
||||
+88
@@ -0,0 +1,88 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class InferParentWithinMethodArgumentNode
|
||||
extends AbstractInferParentFromMethodNode {
|
||||
private final int argIndex;
|
||||
|
||||
public InferParentWithinMethodArgumentNode(
|
||||
SourceSection sourceSection, VmLanguage language, int argIndex) {
|
||||
super(sourceSection, language);
|
||||
this.argIndex = argIndex;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private int getMethodSlot(FrameDescriptor frameDescriptor) {
|
||||
var methodSlot = frameDescriptor.getAuxiliarySlots().get(VmUtils.METHOD_FRAME_SLOT_ID);
|
||||
if (methodSlot == null) {
|
||||
// used in intrinsic constructor e.g. pkl.base#List()
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
return methodSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Method getMethod(VirtualFrame frame) {
|
||||
var method = (Method) frame.getAuxiliarySlot(getMethodSlot(frame.getFrameDescriptor()));
|
||||
if (method == null) {
|
||||
// used in FunctionN.apply()
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
}
|
||||
|
||||
return method;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getParameterTypeNode(frame, argIndex);
|
||||
}
|
||||
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
|
||||
@Specialization(
|
||||
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
Object defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame) {
|
||||
var method = getMethod(frame);
|
||||
var typeNode = getTypeNode(frame, method);
|
||||
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
|
||||
}
|
||||
}
|
||||
+33
-34
@@ -15,66 +15,65 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
|
||||
/** Infers the parent to amend in `function createPerson(): Person = new { ... }`. */
|
||||
public final class InferParentWithinMethodNode extends ExpressionNode {
|
||||
private final VmLanguage language;
|
||||
public abstract class InferParentWithinMethodNode extends AbstractInferParentFromMethodNode {
|
||||
private final Identifier methodName;
|
||||
@Child private @Nullable ExpressionNode ownerNode;
|
||||
@CompilationFinal private @Nullable Object inferredParent;
|
||||
@Child private ExpressionNode ownerNode;
|
||||
|
||||
public InferParentWithinMethodNode(
|
||||
protected InferParentWithinMethodNode(
|
||||
SourceSection sourceSection,
|
||||
VmLanguage language,
|
||||
Identifier methodName,
|
||||
ExpressionNode ownerNode) {
|
||||
|
||||
super(sourceSection);
|
||||
this.language = language;
|
||||
super(sourceSection, language);
|
||||
this.methodName = methodName;
|
||||
this.ownerNode = ownerNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
if (inferredParent != null) return inferredParent;
|
||||
|
||||
// remaining code only runs first time this node is executed
|
||||
// (assuming evaluation isn't continued despite errors)
|
||||
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
assert ownerNode != null;
|
||||
protected Method getMethod(VirtualFrame frame) {
|
||||
var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
|
||||
assert owner.isPrototype();
|
||||
|
||||
var method = owner.getVmClass().getDeclaredMethod(methodName);
|
||||
assert method != null;
|
||||
return method;
|
||||
}
|
||||
|
||||
var returnTypeNode = method.getReturnTypeNode();
|
||||
if (returnTypeNode == null || returnTypeNode instanceof UnknownTypeNode) {
|
||||
inferredParent = VmDynamic.empty();
|
||||
ownerNode = null;
|
||||
return inferredParent;
|
||||
}
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getReturnTypeNode(frame);
|
||||
}
|
||||
|
||||
var returnTypeDefaultValue =
|
||||
returnTypeNode.createDefaultValue(
|
||||
frame, language, method.getHeaderSection(), method.getQualifiedName());
|
||||
if (returnTypeDefaultValue != null) {
|
||||
inferredParent = returnTypeDefaultValue;
|
||||
ownerNode = null;
|
||||
return inferredParent;
|
||||
}
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
@Specialization(
|
||||
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
Object defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame) {
|
||||
var method = getMethod(frame);
|
||||
var typeNode = getTypeNode(frame, method);
|
||||
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
|
||||
}
|
||||
}
|
||||
|
||||
+33
-35
@@ -15,34 +15,31 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.ast.type.TypeNode.UnknownTypeNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmDynamic;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
/** Infers the parent to amend in `obj { local function createPerson(): Person = new { ... } }`. */
|
||||
public final class InferParentWithinObjectMethodNode extends ExpressionNode {
|
||||
private final VmLanguage language;
|
||||
public abstract class InferParentWithinObjectMethodNode extends AbstractInferParentFromMethodNode {
|
||||
private final Identifier localMethodName;
|
||||
@Child private @Nullable ExpressionNode ownerNode;
|
||||
@CompilationFinal private @Nullable Object inferredParent;
|
||||
@Child private ExpressionNode ownerNode;
|
||||
|
||||
public InferParentWithinObjectMethodNode(
|
||||
protected InferParentWithinObjectMethodNode(
|
||||
SourceSection sourceSection,
|
||||
VmLanguage language,
|
||||
Identifier localMethodName,
|
||||
ExpressionNode ownerNode) {
|
||||
|
||||
super(sourceSection);
|
||||
this.language = language;
|
||||
super(sourceSection, language);
|
||||
this.localMethodName = localMethodName;
|
||||
this.ownerNode = ownerNode;
|
||||
|
||||
@@ -50,15 +47,7 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
if (inferredParent != null) return inferredParent;
|
||||
|
||||
// remaining code only runs first time this node is executed
|
||||
// (assuming evaluation isn't continued despite errors)
|
||||
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
assert ownerNode != null;
|
||||
protected Method getMethod(VirtualFrame frame) {
|
||||
var owner = (VmObjectLike) ownerNode.executeGeneric(frame);
|
||||
|
||||
var member = owner.getMember(localMethodName);
|
||||
@@ -66,23 +55,32 @@ public final class InferParentWithinObjectMethodNode extends ExpressionNode {
|
||||
|
||||
var methodNode = (ObjectMethodNode) member.getMemberNode();
|
||||
assert methodNode != null;
|
||||
return methodNode;
|
||||
}
|
||||
|
||||
var returnTypeNode = methodNode.getReturnTypeNode();
|
||||
if (returnTypeNode == null || returnTypeNode instanceof UnknownTypeNode) {
|
||||
inferredParent = VmDynamic.empty();
|
||||
ownerNode = null;
|
||||
return inferredParent;
|
||||
}
|
||||
@Override
|
||||
protected @Nullable TypeNode getTypeNode(VirtualFrame frame, Method method) {
|
||||
return method.getReturnTypeNode(frame);
|
||||
}
|
||||
|
||||
Object defaultReturnTypeValue =
|
||||
returnTypeNode.createDefaultValue(
|
||||
frame, language, member.getHeaderSection(), member.getQualifiedName());
|
||||
if (defaultReturnTypeValue != null) {
|
||||
inferredParent = defaultReturnTypeValue;
|
||||
ownerNode = null;
|
||||
return inferredParent;
|
||||
}
|
||||
// keep specializations in sync with other AbstractInferParentFromMethodNode subclasses
|
||||
|
||||
throw exceptionBuilder().evalError("cannotInferParent").build();
|
||||
@Specialization(
|
||||
guards = {"getMethod(frame) == cachedMethod", "isFinalType(cachedMethod, typeNode)"})
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Cached("getMethod(frame)") @SuppressWarnings("unused") Method cachedMethod,
|
||||
@Cached("getTypeNode(frame, cachedMethod)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
"getDefaultValue(frame, typeNode, cachedMethod.getHeaderSection(), cachedMethod.getQualifiedName())")
|
||||
Object defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame) {
|
||||
var method = getMethod(frame);
|
||||
var typeNode = getTypeNode(frame, method);
|
||||
return getDefaultValue(frame, typeNode, method.getHeaderSection(), method.getQualifiedName());
|
||||
}
|
||||
}
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -45,9 +45,9 @@ public final class InvokeLexicalClassMethodNode extends AbstractInvokeLexicalMet
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget getCallTarget(VmObjectLike owner) {
|
||||
protected Method getMethod(VmObjectLike owner) {
|
||||
var method = owner.getVmClass().getDeclaredMethod(methodName);
|
||||
assert method != null;
|
||||
return method.getCallTarget(getSourceSection());
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -43,9 +44,11 @@ public final class InvokeLexicalObjectMethodNode extends AbstractInvokeLexicalMe
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget getCallTarget(VmObjectLike owner) {
|
||||
var method = owner.getMember(methodName);
|
||||
assert method != null && method.isLocal();
|
||||
return (CallTarget) method.getCallTarget().call(owner, owner);
|
||||
protected Method getMethod(VmObjectLike owner) {
|
||||
var member = owner.getMember(methodName);
|
||||
assert member != null && member.isLocal();
|
||||
var method = (ObjectMethodNode) member.getMemberNode();
|
||||
assert method != null;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
+5
-13
@@ -17,17 +17,16 @@ package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.ClassMethod;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
/** A non-virtual ("direct") method call. Used only for methods on {@code pkl:base}. */
|
||||
public final class InvokeMethodDirectNode extends ExpressionNode {
|
||||
public final class InvokeMethodDirectNode extends AbstractInvokeMethodNode {
|
||||
private final ClassMethod method;
|
||||
private final VmObjectLike owner;
|
||||
@Child private ExpressionNode receiverNode;
|
||||
@Children private final ExpressionNode[] argumentNodes;
|
||||
|
||||
@Child private DirectCallNode callNode;
|
||||
|
||||
@@ -37,24 +36,17 @@ public final class InvokeMethodDirectNode extends ExpressionNode {
|
||||
ExpressionNode receiverNode,
|
||||
ExpressionNode[] argumentNodes) {
|
||||
|
||||
super(sourceSection);
|
||||
super(sourceSection, argumentNodes);
|
||||
this.method = method;
|
||||
this.owner = method.getOwner();
|
||||
this.receiverNode = receiverNode;
|
||||
this.argumentNodes = argumentNodes;
|
||||
|
||||
callNode = DirectCallNode.create(method.getCallTarget(sourceSection));
|
||||
}
|
||||
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiverNode.executeGeneric(frame);
|
||||
args[1] = owner;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
var args = evalArgs(frame, method, owner, receiverNode.executeGeneric(frame));
|
||||
return callNode.call(args);
|
||||
}
|
||||
}
|
||||
|
||||
+6
-40
@@ -26,7 +26,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.instrumentation.GenerateWrapper;
|
||||
import com.oracle.truffle.api.instrumentation.ProbeNode;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.nodes.IndirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
@@ -43,9 +42,8 @@ import org.pkl.core.runtime.VmFunction;
|
||||
@NodeChild(value = "receiverNode", type = ExpressionNode.class)
|
||||
@NodeChild(value = "receiverClassNode", type = GetClassNode.class, executeWith = "receiverNode")
|
||||
@GenerateWrapper
|
||||
public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
public abstract class InvokeMethodVirtualNode extends AbstractInvokeMethodNode {
|
||||
protected final Identifier methodName;
|
||||
@Children private final ExpressionNode[] argumentNodes;
|
||||
private final MemberLookupMode lookupMode;
|
||||
private final boolean needsConst;
|
||||
|
||||
@@ -56,9 +54,8 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
MemberLookupMode lookupMode,
|
||||
boolean needsConst) {
|
||||
|
||||
super(sourceSection);
|
||||
super(sourceSection, argumentNodes);
|
||||
this.methodName = methodName;
|
||||
this.argumentNodes = argumentNodes;
|
||||
this.lookupMode = lookupMode;
|
||||
this.needsConst = needsConst;
|
||||
}
|
||||
@@ -78,7 +75,6 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
public abstract Object executeWith(VirtualFrame frame, Object value, VmClass clazz);
|
||||
|
||||
/** Intrinsifies `FunctionN.apply()` calls. */
|
||||
@ExplodeLoop
|
||||
@Specialization(guards = {"methodName == APPLY", "receiver.getCallTarget() == cachedCallTarget"})
|
||||
protected Object evalFunctionCached(
|
||||
VirtualFrame frame,
|
||||
@@ -87,37 +83,21 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
@Cached("receiver.getCallTarget()") @SuppressWarnings("unused")
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver.getThisValue();
|
||||
args[1] = receiver;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
var args = evalArgs(frame, null, receiver, receiver.getThisValue());
|
||||
return callNode.call(args);
|
||||
}
|
||||
|
||||
/** Intrinsifies `FunctionN.apply()` calls. */
|
||||
@ExplodeLoop
|
||||
@Specialization(guards = "methodName == APPLY", replaces = "evalFunctionCached")
|
||||
protected Object evalFunction(
|
||||
VirtualFrame frame,
|
||||
VmFunction receiver,
|
||||
@SuppressWarnings("unused") VmClass receiverClass,
|
||||
@Exclusive @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver.getThisValue();
|
||||
args[1] = receiver;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
var args = evalArgs(frame, null, receiver, receiver.getThisValue());
|
||||
return callNode.call(receiver.getCallTarget(), args);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
@Specialization(guards = "receiverClass == cachedReceiverClass")
|
||||
protected Object evalCached(
|
||||
VirtualFrame frame,
|
||||
@@ -126,32 +106,18 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
@Cached("receiverClass") @SuppressWarnings("unused") VmClass cachedReceiverClass,
|
||||
@Cached("resolveMethod(receiverClass)") ClassMethod method,
|
||||
@Cached("create(method.getCallTarget(sourceSection))") DirectCallNode callNode) {
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver;
|
||||
args[1] = method.getOwner();
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
var args = evalArgs(frame, method, method.getOwner(), receiver);
|
||||
return callNode.call(args);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected Object eval(
|
||||
VirtualFrame frame,
|
||||
Object receiver,
|
||||
VmClass receiverClass,
|
||||
@Exclusive @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
var method = resolveMethod(receiverClass);
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver;
|
||||
args[1] = method.getOwner();
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
var args = evalArgs(frame, method, method.getOwner(), receiver);
|
||||
|
||||
// Deprecation should not report here (getCallTarget(sourceSection)), as this happens for each
|
||||
// and every call.
|
||||
|
||||
+3
-3
@@ -15,9 +15,9 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -42,9 +42,9 @@ public final class InvokeQualifiedClassMethodNode extends AbstractInvokeQualifie
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget getCallTarget(VmObjectLike owner) {
|
||||
protected Method getMethod(VmObjectLike owner) {
|
||||
var method = owner.getVmClass().getDeclaredMethod(methodName);
|
||||
assert method != null;
|
||||
return method.getCallTarget(getSourceSection());
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
+8
-5
@@ -15,10 +15,11 @@
|
||||
*/
|
||||
package org.pkl.core.ast.expression.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.member.Method;
|
||||
import org.pkl.core.ast.member.ObjectMethodNode;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
@@ -43,9 +44,11 @@ public final class InvokeQualifiedObjectMethodNode extends AbstractInvokeQualifi
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget getCallTarget(VmObjectLike owner) {
|
||||
var method = owner.getMember(methodName);
|
||||
assert method != null && method.isLocal();
|
||||
return (CallTarget) method.getCallTarget().call(owner, owner);
|
||||
protected Method getMethod(VmObjectLike owner) {
|
||||
var member = owner.getMember(methodName);
|
||||
assert member != null && member.isLocal();
|
||||
var method = (ObjectMethodNode) member.getMemberNode();
|
||||
assert method != null;
|
||||
return method;
|
||||
}
|
||||
}
|
||||
|
||||
+4
-14
@@ -20,7 +20,6 @@ import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.ClassMethod;
|
||||
@@ -28,9 +27,8 @@ import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class InvokeSuperMethodNode extends ExpressionNode {
|
||||
public abstract class InvokeSuperMethodNode extends AbstractInvokeMethodNode {
|
||||
private final Identifier methodName;
|
||||
@Children private final ExpressionNode[] argumentNodes;
|
||||
private final boolean needsConst;
|
||||
|
||||
protected InvokeSuperMethodNode(
|
||||
@@ -39,29 +37,21 @@ public abstract class InvokeSuperMethodNode extends ExpressionNode {
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean needsConst) {
|
||||
|
||||
super(sourceSection);
|
||||
super(sourceSection, argumentNodes);
|
||||
this.needsConst = needsConst;
|
||||
|
||||
assert !methodName.isLocalMethod();
|
||||
|
||||
this.methodName = methodName;
|
||||
this.argumentNodes = argumentNodes;
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
@Specialization
|
||||
protected Object eval(
|
||||
VirtualFrame frame,
|
||||
@Cached(value = "findSupermethod(frame)", neverDefault = true) ClassMethod supermethod,
|
||||
@Cached("create(supermethod.getCallTarget(sourceSection))") DirectCallNode callNode) {
|
||||
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = VmUtils.getReceiverOrNull(frame);
|
||||
args[1] = supermethod.getOwner();
|
||||
for (int i = 0; i < argumentNodes.length; i++) {
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
var args =
|
||||
evalArgs(frame, supermethod, supermethod.getOwner(), VmUtils.getReceiverOrNull(frame));
|
||||
return callNode.call(args);
|
||||
}
|
||||
|
||||
|
||||
+1
-2
@@ -21,7 +21,6 @@ import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.SimpleRootNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
@@ -54,7 +53,7 @@ public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
|
||||
int[] parameterSlots) {
|
||||
super(sourceSection);
|
||||
this.expressionNode = expressionNode;
|
||||
frameDescriptor.findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
frameDescriptor.findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
|
||||
var rootNode =
|
||||
new SimpleRootNode(
|
||||
VmLanguage.get(this),
|
||||
|
||||
@@ -18,6 +18,7 @@ package org.pkl.core.ast.member;
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.List;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
@@ -28,7 +29,7 @@ import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
public final class ClassMethod extends ClassMember {
|
||||
public final class ClassMethod extends ClassMember implements Method {
|
||||
private final List<TypeParameter> typeParameters;
|
||||
|
||||
// null = not deprecated, "" = no/empty message in the @Deprecated body
|
||||
@@ -91,6 +92,11 @@ public final class ClassMethod extends ClassMember {
|
||||
return functionNode.getCallTarget();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner) {
|
||||
return getCallTarget(callSite);
|
||||
}
|
||||
|
||||
public int getParameterCount() {
|
||||
return functionNode.getParameterCount();
|
||||
}
|
||||
@@ -99,6 +105,11 @@ public final class ClassMethod extends ClassMember {
|
||||
return functionNode.getReturnTypeNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
|
||||
return functionNode.getReturnTypeNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public String getCallSignature() {
|
||||
return functionNode.getCallSignature();
|
||||
@@ -131,4 +142,8 @@ public final class ClassMethod extends ClassMember {
|
||||
public PClass.Method export(PClass owner) {
|
||||
return functionNode.export(owner, docComment, annotations, modifiers, typeParameters);
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
|
||||
return functionNode.getParameterTypeNode(idx);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -81,6 +81,11 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
return paramCount;
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getParameterTypeNode(int idx) {
|
||||
if (idx >= paramCount) return null;
|
||||
return parameterTypeNodes[idx];
|
||||
}
|
||||
|
||||
public @Nullable TypeNode getReturnTypeNode() {
|
||||
return returnTypeNode;
|
||||
}
|
||||
@@ -176,7 +181,7 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
assert argCount != paramCount;
|
||||
|
||||
return exceptionBuilder()
|
||||
.evalError("wrongFunctionArgumentCount", paramCount, argCount)
|
||||
.evalError("wrongFunctionArgumentCount", paramCount, argCount, paramCount == 1 ? "" : "s")
|
||||
.withSourceSection(member.getHeaderSection())
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.member;
|
||||
|
||||
import com.oracle.truffle.api.CallTarget;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.runtime.VmObjectLike;
|
||||
|
||||
public interface Method {
|
||||
CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner);
|
||||
|
||||
@Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx);
|
||||
|
||||
@Nullable TypeNode getReturnTypeNode(VirtualFrame frame);
|
||||
|
||||
SourceSection getHeaderSection();
|
||||
|
||||
String getQualifiedName();
|
||||
}
|
||||
@@ -20,13 +20,14 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
|
||||
public final class ObjectMethodNode extends RegularMemberNode {
|
||||
public final class ObjectMethodNode extends RegularMemberNode implements Method {
|
||||
private final VmLanguage language;
|
||||
private final int parameterCount;
|
||||
@Children private final @Nullable UnresolvedTypeNode[] unresolvedParameterTypeNodes;
|
||||
@@ -57,29 +58,50 @@ public final class ObjectMethodNode extends RegularMemberNode {
|
||||
return functionNode.getReturnTypeNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getReturnTypeNode(VirtualFrame frame) {
|
||||
return getFunctionNode(frame, true).getReturnTypeNode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public CallTarget getCallTarget(SourceSection callSite, VmObjectLike owner) {
|
||||
return (CallTarget) getCallTarget().call(owner, owner);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable TypeNode getParameterTypeNode(VirtualFrame frame, int idx) {
|
||||
return getFunctionNode(frame, true).getParameterTypeNode(idx);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected CallTarget executeImpl(VirtualFrame frame) {
|
||||
if (functionNode == null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
return getFunctionNode(frame, false).getCallTarget();
|
||||
}
|
||||
|
||||
var parameterTypeNodes =
|
||||
VmUtils.resolveParameterTypes(frame, getFrameDescriptor(), unresolvedParameterTypeNodes);
|
||||
private FunctionNode getFunctionNode(VirtualFrame frame, boolean isPreInit) {
|
||||
if (functionNode != null) return functionNode;
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
|
||||
var returnTypeNode =
|
||||
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null;
|
||||
|
||||
functionNode =
|
||||
new FunctionNode(
|
||||
language,
|
||||
getFrameDescriptor(),
|
||||
member,
|
||||
parameterCount,
|
||||
parameterTypeNodes,
|
||||
returnTypeNode,
|
||||
true,
|
||||
bodyNode);
|
||||
if (isPreInit) {
|
||||
// TODO: with VmType/CreateDefaultValueNode this may be removable
|
||||
adoptChildren();
|
||||
}
|
||||
var parameterTypeNodes =
|
||||
VmUtils.resolveParameterTypes(frame, getFrameDescriptor(), unresolvedParameterTypeNodes);
|
||||
|
||||
return functionNode.getCallTarget();
|
||||
var returnTypeNode =
|
||||
unresolvedReturnTypeNode != null ? unresolvedReturnTypeNode.execute(frame) : null;
|
||||
|
||||
functionNode =
|
||||
new FunctionNode(
|
||||
language,
|
||||
getFrameDescriptor(),
|
||||
member,
|
||||
parameterCount,
|
||||
parameterTypeNodes,
|
||||
returnTypeNode,
|
||||
true,
|
||||
bodyNode);
|
||||
return functionNode;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -65,15 +65,15 @@ public final class PropertyTypeNode extends PklRootNode {
|
||||
}
|
||||
|
||||
public @Nullable Object getDefaultValue(VirtualFrame frame) {
|
||||
if (!defaultValueInitialized) {
|
||||
defaultValue =
|
||||
typeNode.createDefaultValue(
|
||||
frame, VmLanguage.get(this), getSourceSection(), qualifiedPropertyName);
|
||||
// can't cache default value for `module` type in a non-final module because it's a self-type
|
||||
// (the default value changes when inherited).
|
||||
if (typeNode.isFinalType()) {
|
||||
defaultValueInitialized = true;
|
||||
}
|
||||
if (defaultValueInitialized) return defaultValue;
|
||||
|
||||
defaultValue =
|
||||
typeNode.createDefaultValue(
|
||||
frame, VmLanguage.get(this), getSourceSection(), qualifiedPropertyName);
|
||||
// can't cache default value for `module` type in a non-final module because it's a self-type
|
||||
// (the default value changes when inherited).
|
||||
if (typeNode.isFinalType()) {
|
||||
defaultValueInitialized = true;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
@@ -16,31 +16,34 @@
|
||||
package org.pkl.core.ast.type;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.dsl.Bind;
|
||||
import com.oracle.truffle.api.dsl.Cached;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.expression.member.AbstractInferParentNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.LateInit;
|
||||
|
||||
/** Resolves `<type>` to the type's default value in `new <type> { ... }`. */
|
||||
public final class GetParentForTypeNode extends ExpressionNode {
|
||||
@Child private UnresolvedTypeNode unresolvedTypeNode;
|
||||
public abstract class GetParentForTypeNode extends AbstractInferParentNode {
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedTypeNode;
|
||||
@Child private @Nullable TypeNode typeNode;
|
||||
private final String qualifiedName;
|
||||
protected final String qualifiedName;
|
||||
|
||||
@CompilationFinal @LateInit Object defaultValue;
|
||||
|
||||
public GetParentForTypeNode(
|
||||
SourceSection sourceSection, UnresolvedTypeNode unresolvedTypeNode, String qualifiedName) {
|
||||
super(sourceSection);
|
||||
protected GetParentForTypeNode(
|
||||
SourceSection sourceSection,
|
||||
VmLanguage language,
|
||||
UnresolvedTypeNode unresolvedTypeNode,
|
||||
String qualifiedName) {
|
||||
super(sourceSection, language);
|
||||
this.unresolvedTypeNode = unresolvedTypeNode;
|
||||
this.qualifiedName = qualifiedName;
|
||||
}
|
||||
|
||||
private TypeNode getTypeNode(VirtualFrame frame) {
|
||||
protected TypeNode getTypeNode(VirtualFrame frame) {
|
||||
if (typeNode == null) {
|
||||
assert unresolvedTypeNode != null;
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
typeNode = unresolvedTypeNode.execute(frame);
|
||||
adoptChildren();
|
||||
@@ -48,33 +51,19 @@ public final class GetParentForTypeNode extends ExpressionNode {
|
||||
return typeNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
//noinspection ConstantValue
|
||||
if (defaultValue != null) return defaultValue;
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
@Specialization(guards = {"typeNode.isFinalType()"})
|
||||
protected final Object evalCached(
|
||||
@SuppressWarnings("unused") VirtualFrame frame,
|
||||
@Bind("getTypeNode(frame)") @SuppressWarnings("unused") TypeNode typeNode,
|
||||
@Cached(
|
||||
value = "getDefaultValue(frame, typeNode, sourceSection, qualifiedName)",
|
||||
neverDefault = true)
|
||||
Object defaultValue) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
var typeNode = getTypeNode(frame);
|
||||
var defaultValue =
|
||||
typeNode.createDefaultValue(frame, VmLanguage.get(this), sourceSection, qualifiedName);
|
||||
|
||||
// can't cache default value for `module`/`this` types in a non-final modules/classes because
|
||||
// they're self types (the default value changes when inherited).
|
||||
if (typeNode.isFinalType() && defaultValue != null) {
|
||||
unresolvedTypeNode = null;
|
||||
this.defaultValue = defaultValue;
|
||||
}
|
||||
|
||||
if (defaultValue != null) {
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
// try to produce a more specific error message than "cannotInstantiateType"
|
||||
var clazz = typeNode.getVmClass();
|
||||
if (clazz != null) VmUtils.checkIsInstantiable(clazz, typeNode);
|
||||
|
||||
throw exceptionBuilder()
|
||||
.evalError("cannotInstantiateType", typeNode.getSourceSection().getCharacters())
|
||||
.build();
|
||||
@Specialization(replaces = "evalCached")
|
||||
protected final Object eval(VirtualFrame frame, @Bind("getTypeNode(frame)") TypeNode typeNode) {
|
||||
return getDefaultValue(frame, typeNode, sourceSection, qualifiedName);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,7 +57,7 @@ public final class IdentityMixinNode extends PklRootNode {
|
||||
if (arguments.length != 3) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
.evalError("wrongFunctionArgumentCount", 1, arguments.length - 2)
|
||||
.evalError("wrongFunctionArgumentCount", 1, arguments.length - 2, "")
|
||||
.withSourceSection(sourceSection)
|
||||
.build();
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ import org.pkl.core.PklBugException;
|
||||
import org.pkl.core.StackFrame;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.*;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
||||
import org.pkl.core.ast.expression.primary.GetReceiverClassNode;
|
||||
import org.pkl.core.ast.expression.primary.GetReceiverNode;
|
||||
@@ -2906,25 +2905,21 @@ public abstract class TypeNode extends PklNode {
|
||||
return this;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private int getCustomThisSlot(FrameDescriptor frameDescriptor) {
|
||||
// can't store the slot id as this node may be called from different root nodes
|
||||
// (see constraints14 snippet)
|
||||
return frameDescriptor.findOrAddAuxiliarySlot(VmUtils.CUSTOM_THIS_FRAME_SLOT_ID);
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
protected Object executeLazily(VirtualFrame frame, Object value) {
|
||||
int customThisSlot;
|
||||
var numberOfAuxiliarySlots = frame.getFrameDescriptor().getNumberOfAuxiliarySlots();
|
||||
if (numberOfAuxiliarySlots == 0) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
customThisSlot =
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
} else {
|
||||
// assertion: we only use auxiliary slots for custom `this`.
|
||||
assert numberOfAuxiliarySlots == 1;
|
||||
customThisSlot = 0;
|
||||
}
|
||||
var ret = childNode.executeLazily(frame, value);
|
||||
|
||||
var localContext = language.localContext.get();
|
||||
var prevShouldTypeCheck = localContext.shouldEagerTypecheck();
|
||||
localContext.shouldEagerTypecheck(true);
|
||||
frame.setAuxiliarySlot(customThisSlot, value);
|
||||
frame.setAuxiliarySlot(getCustomThisSlot(frame.getFrameDescriptor()), value);
|
||||
try {
|
||||
for (var node : constraintNodes) {
|
||||
node.execute(frame);
|
||||
|
||||
@@ -52,7 +52,6 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.SimpleRootNode;
|
||||
import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.builder.AstBuilder;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.expression.primary.CustomThisNode;
|
||||
import org.pkl.core.ast.expression.primary.ThisNode;
|
||||
import org.pkl.core.ast.member.*;
|
||||
@@ -77,6 +76,22 @@ public final class VmUtils {
|
||||
|
||||
public static final URI REPL_TEXT_URI = URI.create(REPL_TEXT);
|
||||
|
||||
public static final Object CUSTOM_THIS_FRAME_SLOT_ID =
|
||||
new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "customThisSlot";
|
||||
}
|
||||
};
|
||||
|
||||
public static final Object METHOD_FRAME_SLOT_ID =
|
||||
new Object() {
|
||||
@Override
|
||||
public String toString() {
|
||||
return "method";
|
||||
}
|
||||
};
|
||||
|
||||
private static final Engine PKL_ENGINE =
|
||||
Engine.newBuilder("pkl").option("engine.WarnInterpreterOnly", "false").build();
|
||||
|
||||
@@ -1066,7 +1081,7 @@ public final class VmUtils {
|
||||
}
|
||||
|
||||
public static int findCustomThisSlot(VirtualFrame frame) {
|
||||
var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CustomThisScope.FRAME_SLOT_ID);
|
||||
var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CUSTOM_THIS_FRAME_SLOT_ID);
|
||||
assert result != null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -310,7 +310,7 @@ cannotDefineExternalMember=\
|
||||
External members can only be defined by standard library modules.
|
||||
|
||||
wrongFunctionArgumentCount=\
|
||||
Expected {0} function arguments but got {1}.
|
||||
Expected {0} function argument{2} but got {1}.
|
||||
|
||||
noOuterScope=\
|
||||
Top-level scope does not have an outer scope.
|
||||
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
open module inferParameterType
|
||||
|
||||
import "pkl:test"
|
||||
|
||||
hidden x: Int
|
||||
|
||||
open class Foo extends module {
|
||||
function bar(baz: Foo): Int = super.bar(baz) * this.x
|
||||
|
||||
function superBar(baz: Foo): Int = super.bar(new { x = 5 + baz.x }) * this.x
|
||||
|
||||
function thisBar(baz: Foo): Int = this.x + baz.x
|
||||
}
|
||||
|
||||
class Qux extends Foo {
|
||||
function bar(baz: Foo): Int = thisBar(new { x = baz.x })
|
||||
}
|
||||
|
||||
function bar(baz: Foo): Int = baz.x
|
||||
|
||||
const function outerMethod(a: Int, b: Qux) = a + b.x * 2
|
||||
|
||||
local quux = (a: Foo) -> a.x
|
||||
|
||||
|
||||
qualified = this.bar(new { x = 1 })
|
||||
unqualifiedLexical = bar(new { x = 1 })
|
||||
unqualifiedThis = new Qux { x = 7 }.bar(new { x = 1 })
|
||||
`super` = new Foo { x = 2 }.superBar(new Foo { x = 3 })
|
||||
nestedMethodCalls = outerMethod(bar(new { x = 8 }), new { x = 9 })
|
||||
objectMethod {
|
||||
local function corge(prop: Foo) = prop
|
||||
call = corge(new { x = 0 }).x
|
||||
tooManyParams = test.catch(() -> corge(new {}, new {}))
|
||||
}
|
||||
|
||||
intrinsicConstructor = test.catch(() -> List(new {}))
|
||||
genericMethod = test.catch(() -> Pair(new {}, new {}))
|
||||
fnApply = test.catch(() -> quux.apply(new {}))
|
||||
tooManyParams = test.catch(() -> bar(new {}, new {}))
|
||||
|
||||
class Bar1 { x: Int = 1 }
|
||||
class Bar2 { x: Int = 2 }
|
||||
class C1 { function f(p: Bar1) = p.x }
|
||||
class C2 { function f(p: Bar2) = p.x * 10 }
|
||||
|
||||
local function call(a) = a.f(new {}) // poly call
|
||||
|
||||
polyCall1 = call(new C1 {})
|
||||
polyCall2 = call(new C2 {})
|
||||
@@ -139,18 +139,18 @@ one {
|
||||
res31 {
|
||||
name = "Other"
|
||||
}
|
||||
res32 = "Expected 1 function arguments but got 2."
|
||||
res32 = "Expected 1 function argument but got 2."
|
||||
res33 {
|
||||
name = "Override"
|
||||
}
|
||||
res34 = "Expected 1 function arguments but got 2."
|
||||
res34 = "Expected 1 function argument but got 2."
|
||||
res35 {
|
||||
name = "Other"
|
||||
}
|
||||
res36 = "Expected 1 function arguments but got 2."
|
||||
res36 = "Expected 1 function argument but got 2."
|
||||
res37 = "Expected value of type `new#Person`, but got type `Int`. Value: 1"
|
||||
res38 {
|
||||
name = "Override"
|
||||
}
|
||||
res39 = "Expected 1 function arguments but got 2."
|
||||
res39 = "Expected 1 function argument but got 2."
|
||||
res40 = "Expected value of type `new#Person`, but got type `Int`. Value: 1"
|
||||
|
||||
@@ -63,20 +63,20 @@ res8 {
|
||||
res9 {
|
||||
name = "Other"
|
||||
}
|
||||
res9b = "Expected 1 function arguments but got 2."
|
||||
res9b = "Expected 1 function argument but got 2."
|
||||
res9c {
|
||||
name = "Override"
|
||||
}
|
||||
res9d = "Expected 1 function arguments but got 2."
|
||||
res9d = "Expected 1 function argument but got 2."
|
||||
res10 {
|
||||
name = "Other"
|
||||
}
|
||||
res10b = "Expected 1 function arguments but got 2."
|
||||
res10b = "Expected 1 function argument but got 2."
|
||||
res10c = "Expected value of type `newType#Person`, but got type `Int`. Value: 1"
|
||||
res10d {
|
||||
name = "Override"
|
||||
}
|
||||
res10e = "Expected 1 function arguments but got 2."
|
||||
res10e = "Expected 1 function argument but got 2."
|
||||
res10f = "Expected value of type `newType#Person`, but got type `Int`. Value: 1"
|
||||
res11 {
|
||||
name = "Pigeon"
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
qualified = 1
|
||||
unqualifiedLexical = 1
|
||||
unqualifiedThis = 8
|
||||
`super` = 16
|
||||
nestedMethodCalls = 26
|
||||
objectMethod {
|
||||
call = 0
|
||||
tooManyParams = "Expected 1 function argument but got 2."
|
||||
}
|
||||
intrinsicConstructor = "Cannot tell which parent to amend."
|
||||
genericMethod = "Cannot tell which parent to amend."
|
||||
fnApply = "Cannot tell which parent to amend."
|
||||
tooManyParams = "Expected 1 function argument but got 2."
|
||||
polyCall1 = 1
|
||||
polyCall2 = 20
|
||||
Reference in New Issue
Block a user