mirror of
https://github.com/apple/pkl.git
synced 2026-04-17 14:09:48 +02:00
Overhaul implementation of for-generators (#844)
Motivation:
* fix known bugs and limitations of for-generators
* improve code health by removing complex workarounds
Changes:
* simplify AstBuilder code related to for-generators
* track for-generators via `SymbolTable.enterForGenerator()`
* add `RestoreForBindingsNode` during initial AST construction
instead of calling `MemberNode.replaceBody()` later on
* simplify some unnecessarily complex code
* remove workarounds and band-aids such as:
* `isInIterable`
* `executeAndSetEagerly`
* adding dummy slots in `AmendFunctionNode`
* overhaul implementation of for-generators
* store keys and values of for-generator iterations in regular instead of auxiliary frame slots
* set them via `TypeNode.executeAndSet()`
* `ResolveVariableNode` no longer needs to search auxiliary slots
* `Read(Enclosing)AuxiliarySlot` is no longer needed
* at the start of each for-generator iteration, create a new `VirtualFrame`
that is a copy of the current frame (arguments + slots)
and stores the iteration key and value in additional slots.
* execute for-generator iteration with the newly created frame
* `childNode.execute(newFrame)`
* Pkl objects created during the iteration will materialize this frame
* store newly created frames in `owner.extraStorage`
if their for-generator slots may be accessed when a generated member is executed
* resolving variable names to for-generator variables at parse time would make this analysis more precise
* when a generated member is executed,
* retrieve the corresponding frame stored in `owner.extraStorage`
* copy the retrieved frame's for-generator slots into slots of the current frame
Result:
* for-generators are implemented in a correct, reasonably simple, and reasonably efficient way
* complexity is fully contained within package `generator` and `AstBuilder`
* for-generator keys and values can be accessed from all nested scopes:
* key and value expressions of generated members
* condition expressions of nested when-generators
* iterable expressions of nested for-generators
* for-generator keys and values can be accessed from within objects created by the expressions listed above
* sibling for-generators can use the same key/value variable names
* parent/child for-generators can use the same key/value variable names
* fixes https://github.com/apple/pkl/issues/741
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -17,7 +17,6 @@ package org.pkl.core.ast;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.function.Function;
|
||||
import org.pkl.core.ast.member.DefaultPropertyBodyNode;
|
||||
import org.pkl.core.runtime.VmExceptionBuilder;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
@@ -43,10 +42,6 @@ public abstract class MemberNode extends PklRootNode {
|
||||
return bodyNode;
|
||||
}
|
||||
|
||||
public final void replaceBody(Function<ExpressionNode, ExpressionNode> replacer) {
|
||||
bodyNode = insert(replacer.apply(bodyNode));
|
||||
}
|
||||
|
||||
protected final VmExceptionBuilder exceptionBuilder() {
|
||||
return new VmExceptionBuilder().withSourceSection(getHeaderSection());
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -55,9 +55,6 @@ public final class VmModifier {
|
||||
|
||||
public static final int GLOB = 0x1000;
|
||||
|
||||
// To be removed when https://github.com/apple/pkl/issues/741 is fixed
|
||||
public static final int IS_IN_ITERABLE = 0x100000;
|
||||
|
||||
// modifier sets
|
||||
|
||||
public static final int NONE = 0;
|
||||
@@ -137,10 +134,6 @@ public final class VmModifier {
|
||||
return (modifiers & ENTRY) != 0;
|
||||
}
|
||||
|
||||
public static boolean isInIterable(int modifiers) {
|
||||
return (modifiers & IS_IN_ITERABLE) != 0;
|
||||
}
|
||||
|
||||
public static boolean isType(int modifiers) {
|
||||
return (modifiers & (CLASS | TYPE_ALIAS | IMPORT)) != 0 && (modifiers & GLOB) == 0;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -25,7 +25,6 @@ import java.io.IOException;
|
||||
import java.net.URI;
|
||||
import java.net.URISyntaxException;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.stream.Collectors;
|
||||
import org.antlr.v4.runtime.ParserRuleContext;
|
||||
@@ -40,7 +39,6 @@ import org.pkl.core.TypeParameter.Variance;
|
||||
import org.pkl.core.ast.*;
|
||||
import org.pkl.core.ast.builder.SymbolTable.AnnotationScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.ClassScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.EntryScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.Scope;
|
||||
import org.pkl.core.ast.expression.binary.*;
|
||||
import org.pkl.core.ast.expression.generator.*;
|
||||
@@ -852,20 +850,20 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
@Override
|
||||
public GeneratorPropertyNode visitObjectProperty(ObjectPropertyContext ctx) {
|
||||
checkHasNoForGenerator(ctx, "forGeneratorCannotGenerateProperties");
|
||||
checkNotInsideForGenerator(ctx, "forGeneratorCannotGenerateProperties");
|
||||
var member = doVisitObjectProperty(ctx);
|
||||
return GeneratorPropertyNodeGen.create(member);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratorMemberNode visitObjectMethod(ObjectMethodContext ctx) {
|
||||
checkHasNoForGenerator(ctx, "forGeneratorCannotGenerateMethods");
|
||||
checkNotInsideForGenerator(ctx, "forGeneratorCannotGenerateMethods");
|
||||
var member = doVisitObjectMethod(ctx);
|
||||
return GeneratorPropertyNodeGen.create(member);
|
||||
}
|
||||
|
||||
private void checkHasNoForGenerator(ParserRuleContext ctx, String errorMessageKey) {
|
||||
if (symbolTable.getCurrentScope().getForGeneratorVariables().isEmpty()) {
|
||||
private void checkNotInsideForGenerator(ParserRuleContext ctx, String errorMessageKey) {
|
||||
if (!symbolTable.getCurrentScope().isForGeneratorScope()) {
|
||||
return;
|
||||
}
|
||||
var forExprCtx = ctx.getParent();
|
||||
@@ -880,12 +878,19 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
@Override
|
||||
public GeneratorMemberNode visitMemberPredicate(MemberPredicateContext ctx) {
|
||||
var keyNodeAndMember = doVisitMemberPredicate(ctx);
|
||||
var keyNode = keyNodeAndMember.first;
|
||||
var member = keyNodeAndMember.second;
|
||||
insertWriteForGeneratorVarsToFrameSlotsNode(member.getMemberNode());
|
||||
if (ctx.err1 == null && ctx.err2 == null) {
|
||||
throw missingDelimiter("]]", ctx.k.stop.getStopIndex() + 1);
|
||||
} else if (ctx.err1 != null
|
||||
&& (ctx.err2 == null || ctx.err1.getStartIndex() != ctx.err2.getStartIndex() - 1)) {
|
||||
// There shouldn't be any whitespace between the first and second ']'.
|
||||
throw wrongDelimiter("]]", "]", ctx.err1.getStartIndex());
|
||||
}
|
||||
|
||||
return GeneratorPredicateMemberNodeGen.create(keyNode, member);
|
||||
var keyNode = symbolTable.enterCustomThisScope(scope -> visitExpr(ctx.k));
|
||||
var member = doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody());
|
||||
var isFrameStored =
|
||||
member.getMemberNode() != null && symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return GeneratorPredicateMemberNodeGen.create(keyNode, member, isFrameStored);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -893,43 +898,23 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var keyNodeAndMember = doVisitObjectEntry(ctx);
|
||||
var keyNode = keyNodeAndMember.first;
|
||||
var member = keyNodeAndMember.second;
|
||||
insertWriteForGeneratorVarsToFrameSlotsNode(member.getMemberNode());
|
||||
|
||||
return GeneratorEntryNodeGen.create(keyNode, member);
|
||||
var isFrameStored =
|
||||
member.getMemberNode() != null && symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return GeneratorEntryNodeGen.create(keyNode, member, isFrameStored);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratorMemberNode visitObjectSpread(ObjectSpreadContext ctx) {
|
||||
var scope = symbolTable.getCurrentScope();
|
||||
var visitingIterable = scope.isVisitingIterable();
|
||||
scope.setVisitingIterable(true);
|
||||
var expr = visitExpr(ctx.expr());
|
||||
scope.setVisitingIterable(visitingIterable);
|
||||
return GeneratorSpreadNodeGen.create(createSourceSection(ctx), expr, ctx.QSPREAD() != null);
|
||||
}
|
||||
|
||||
private void insertWriteForGeneratorVarsToFrameSlotsNode(@Nullable MemberNode memberNode) {
|
||||
if (memberNode == null) return; // member has constant value
|
||||
|
||||
var descriptor = memberNode.getFrameDescriptor();
|
||||
var forGeneratorVars = symbolTable.getCurrentScope().getForGeneratorVariables();
|
||||
if (forGeneratorVars.isEmpty()) {
|
||||
return; // node is not within a for generator
|
||||
}
|
||||
var slots = new int[forGeneratorVars.size()];
|
||||
var i = 0;
|
||||
for (var variable : forGeneratorVars) {
|
||||
slots[i] = descriptor.findOrAddAuxiliarySlot(variable);
|
||||
i++;
|
||||
}
|
||||
memberNode.replaceBody((bodyNode) -> new WriteForVariablesNode(slots, bodyNode));
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratorElementNode visitObjectElement(ObjectElementContext ctx) {
|
||||
var member = doVisitObjectElement(ctx);
|
||||
insertWriteForGeneratorVarsToFrameSlotsNode(member.getMemberNode());
|
||||
return GeneratorElementNodeGen.create(member);
|
||||
var isFrameStored =
|
||||
member.getMemberNode() != null && symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return GeneratorElementNodeGen.create(member, isFrameStored);
|
||||
}
|
||||
|
||||
private GeneratorMemberNode[] doVisitForWhenBody(ObjectBodyContext ctx) {
|
||||
@@ -953,18 +938,6 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return new GeneratorWhenNode(sourceSection, visitExpr(ctx.e), thenNodes, elseNodes);
|
||||
}
|
||||
|
||||
private int pushForGeneratorVariableContext(ParameterContext ctx) {
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
var slot = currentScope.pushForGeneratorVariableContext(ctx);
|
||||
if (slot == -1) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("duplicateDefinition", ctx.typedIdentifier().Identifier().getText())
|
||||
.withSourceSection(createSourceSection(ctx))
|
||||
.build();
|
||||
}
|
||||
return slot;
|
||||
}
|
||||
|
||||
private static boolean isIgnored(@Nullable ParameterContext param) {
|
||||
return param != null && param.UNDERSCORE() != null;
|
||||
}
|
||||
@@ -972,53 +945,68 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
@Override
|
||||
public GeneratorForNode visitForGenerator(ForGeneratorContext ctx) {
|
||||
checkClosingDelimiter(ctx.err, ")", ctx.e.stop);
|
||||
var sourceSection = createSourceSection(ctx);
|
||||
int keyVariableSlot;
|
||||
int valueVariableSlot;
|
||||
UnresolvedTypeNode unresolvedKeyTypeNode;
|
||||
UnresolvedTypeNode unresolvedValueTypeNode;
|
||||
var keyParameter = ctx.t2 == null ? null : ctx.t1;
|
||||
var valueParameter = ctx.t2 == null ? ctx.t1 : ctx.t2;
|
||||
var keyTypedIdentifier = keyParameter == null ? null : keyParameter.typedIdentifier();
|
||||
var valueTypedIdentifier = valueParameter == null ? null : valueParameter.typedIdentifier();
|
||||
var keyIdentifier =
|
||||
keyTypedIdentifier == null ? null : toIdentifier(keyTypedIdentifier.Identifier());
|
||||
var valueIdentifier =
|
||||
valueTypedIdentifier == null ? null : toIdentifier(valueTypedIdentifier.Identifier());
|
||||
if (valueIdentifier != null && valueIdentifier == keyIdentifier) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("duplicateDefinition", valueIdentifier)
|
||||
.withSourceSection(createSourceSection(valueTypedIdentifier.Identifier()))
|
||||
.build();
|
||||
}
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
var ignoreT1 = isIgnored(ctx.t1);
|
||||
var ignoreT2 = ctx.t2 == null ? ignoreT1 : isIgnored(ctx.t2);
|
||||
|
||||
if (ctx.t2 != null) {
|
||||
keyVariableSlot = ignoreT1 ? -1 : pushForGeneratorVariableContext(ctx.t1);
|
||||
valueVariableSlot = ignoreT2 ? -1 : pushForGeneratorVariableContext(ctx.t2);
|
||||
unresolvedKeyTypeNode =
|
||||
ignoreT1 ? null : visitTypeAnnotation(ctx.t1.typedIdentifier().typeAnnotation());
|
||||
unresolvedValueTypeNode =
|
||||
ignoreT2 ? null : visitTypeAnnotation(ctx.t2.typedIdentifier().typeAnnotation());
|
||||
} else {
|
||||
keyVariableSlot = -1;
|
||||
valueVariableSlot = ignoreT1 ? -1 : pushForGeneratorVariableContext(ctx.t1);
|
||||
unresolvedKeyTypeNode = null;
|
||||
unresolvedValueTypeNode =
|
||||
ignoreT1 ? null : visitTypeAnnotation(ctx.t1.typedIdentifier().typeAnnotation());
|
||||
var generatorDescriptorBuilder = currentScope.newFrameDescriptorBuilder();
|
||||
var memberDescriptorBuilder = currentScope.newForGeneratorMemberDescriptorBuilder();
|
||||
var keySlot = -1;
|
||||
var valueSlot = -1;
|
||||
if (keyIdentifier != null) {
|
||||
keySlot = generatorDescriptorBuilder.addSlot(FrameSlotKind.Illegal, keyIdentifier, null);
|
||||
memberDescriptorBuilder.addSlot(FrameSlotKind.Illegal, keyIdentifier, null);
|
||||
}
|
||||
|
||||
var scope = symbolTable.getCurrentScope();
|
||||
var visitingIterable = scope.isVisitingIterable();
|
||||
scope.setVisitingIterable(true);
|
||||
if (valueIdentifier != null) {
|
||||
valueSlot = generatorDescriptorBuilder.addSlot(FrameSlotKind.Illegal, valueIdentifier, null);
|
||||
memberDescriptorBuilder.addSlot(FrameSlotKind.Illegal, valueIdentifier, null);
|
||||
}
|
||||
var unresolvedKeyTypeNode =
|
||||
keyTypedIdentifier == null
|
||||
? null
|
||||
: visitTypeAnnotation(keyTypedIdentifier.typeAnnotation());
|
||||
var unresolvedValueTypeNode =
|
||||
valueTypedIdentifier == null
|
||||
? null
|
||||
: visitTypeAnnotation(valueTypedIdentifier.typeAnnotation());
|
||||
// if possible, initialize immediately to avoid later insert
|
||||
var keyTypeNode =
|
||||
unresolvedKeyTypeNode == null && keySlot != -1
|
||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(keySlot)
|
||||
: null;
|
||||
// if possible, initialize immediately to avoid later insert
|
||||
var valueTypeNode =
|
||||
unresolvedValueTypeNode == null && valueSlot != -1
|
||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(valueSlot)
|
||||
: null;
|
||||
var iterableNode = visitExpr(ctx.e);
|
||||
scope.setVisitingIterable(visitingIterable);
|
||||
var memberNodes = doVisitForWhenBody(ctx.objectBody());
|
||||
if (keyVariableSlot != -1) {
|
||||
currentScope.popForGeneratorVariable();
|
||||
}
|
||||
if (valueVariableSlot != -1) {
|
||||
currentScope.popForGeneratorVariable();
|
||||
}
|
||||
//noinspection ConstantConditions
|
||||
var memberNodes =
|
||||
symbolTable.enterForGenerator(
|
||||
generatorDescriptorBuilder,
|
||||
memberDescriptorBuilder,
|
||||
scope -> doVisitForWhenBody(ctx.objectBody()));
|
||||
return GeneratorForNodeGen.create(
|
||||
sourceSection,
|
||||
keyVariableSlot,
|
||||
valueVariableSlot,
|
||||
createSourceSection(ctx),
|
||||
generatorDescriptorBuilder.build(),
|
||||
iterableNode,
|
||||
unresolvedKeyTypeNode,
|
||||
unresolvedValueTypeNode,
|
||||
memberNodes,
|
||||
ctx.t2 != null && !ignoreT1,
|
||||
!ignoreT2);
|
||||
keyTypeNode,
|
||||
valueTypeNode);
|
||||
}
|
||||
|
||||
private void checkSpaceSeparatedObjectMembers(ObjectBodyContext objectBodyContext) {
|
||||
@@ -1200,15 +1188,13 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
private ObjectMember doVisitObjectElement(ObjectElementContext ctx) {
|
||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return symbolTable.enterEntry(
|
||||
null,
|
||||
scope -> {
|
||||
var elementNode = visitExpr(ctx.expr());
|
||||
|
||||
var modifier =
|
||||
scope.isVisitingIterable()
|
||||
? VmModifier.ELEMENT | VmModifier.IS_IN_ITERABLE
|
||||
: VmModifier.ELEMENT;
|
||||
var modifier = VmModifier.ELEMENT;
|
||||
var member =
|
||||
new ObjectMember(
|
||||
createSourceSection(ctx),
|
||||
@@ -1220,6 +1206,9 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
if (elementNode instanceof ConstantNode constantNode) {
|
||||
member.initConstantValue(constantNode);
|
||||
} else {
|
||||
if (isForGeneratorScope) {
|
||||
elementNode = new RestoreForBindingsNode(elementNode);
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
language, scope.buildFrameDescriptor(), member, elementNode));
|
||||
@@ -1229,21 +1218,6 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
});
|
||||
}
|
||||
|
||||
private Pair<ExpressionNode, ObjectMember> doVisitMemberPredicate(MemberPredicateContext ctx) {
|
||||
if (ctx.err1 == null && ctx.err2 == null) {
|
||||
throw missingDelimiter("]]", ctx.k.stop.getStopIndex() + 1);
|
||||
} else if (ctx.err1 != null
|
||||
&& (ctx.err2 == null || ctx.err1.getStartIndex() != ctx.err2.getStartIndex() - 1)) {
|
||||
// There shouldn't be any whitespace between the first and second ']'.
|
||||
throw wrongDelimiter("]]", "]", ctx.err1.getStartIndex());
|
||||
}
|
||||
|
||||
var keyNode = symbolTable.enterCustomThisScope(scope -> visitExpr(ctx.k));
|
||||
|
||||
return symbolTable.enterEntry(
|
||||
keyNode, objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody()));
|
||||
}
|
||||
|
||||
private Pair<ExpressionNode, ObjectMember> doVisitObjectEntry(ObjectEntryContext ctx) {
|
||||
checkClosingDelimiter(ctx.err1, "]", ctx.k.stop);
|
||||
if (ctx.err2 != null) {
|
||||
@@ -1253,46 +1227,54 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
var keyNode = visitExpr(ctx.k);
|
||||
|
||||
return symbolTable.enterEntry(
|
||||
keyNode, objectMemberInserter(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody()));
|
||||
var member = doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.v, ctx.objectBody());
|
||||
return Pair.of(keyNode, member);
|
||||
}
|
||||
|
||||
private Function<EntryScope, Pair<ExpressionNode, ObjectMember>> objectMemberInserter(
|
||||
private ObjectMember doVisitObjectEntryBody(
|
||||
SourceSection sourceSection,
|
||||
ExpressionNode keyNode,
|
||||
@Nullable ExprContext valueCtx,
|
||||
List<? extends ObjectBodyContext> objectBodyCtxs) {
|
||||
return scope -> {
|
||||
var modifier =
|
||||
scope.isVisitingIterable()
|
||||
? VmModifier.ENTRY | VmModifier.IS_IN_ITERABLE
|
||||
: VmModifier.ENTRY;
|
||||
var member =
|
||||
new ObjectMember(
|
||||
sourceSection, keyNode.getSourceSection(), modifier, null, scope.getQualifiedName());
|
||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return symbolTable.enterEntry(
|
||||
keyNode,
|
||||
scope -> {
|
||||
var modifier = VmModifier.ENTRY;
|
||||
var member =
|
||||
new ObjectMember(
|
||||
sourceSection,
|
||||
keyNode.getSourceSection(),
|
||||
modifier,
|
||||
null,
|
||||
scope.getQualifiedName());
|
||||
if (valueCtx != null) { // ["key"] = value
|
||||
var valueNode = visitExpr(valueCtx);
|
||||
if (valueNode instanceof ConstantNode constantNode) {
|
||||
member.initConstantValue(constantNode);
|
||||
} else {
|
||||
if (isForGeneratorScope) {
|
||||
valueNode = new RestoreForBindingsNode(valueNode);
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
language, scope.buildFrameDescriptor(), member, valueNode));
|
||||
}
|
||||
} else { // ["key"] { ... }
|
||||
var objectBody =
|
||||
doVisitObjectBody(
|
||||
objectBodyCtxs,
|
||||
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
||||
if (isForGeneratorScope) {
|
||||
objectBody = new RestoreForBindingsNode(objectBody);
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
language, scope.buildFrameDescriptor(), member, objectBody));
|
||||
}
|
||||
|
||||
if (valueCtx != null) { // ["key"] = value
|
||||
var valueNode = visitExpr(valueCtx);
|
||||
if (valueNode instanceof ConstantNode constantNode) {
|
||||
member.initConstantValue(constantNode);
|
||||
} else {
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
language, scope.buildFrameDescriptor(), member, valueNode));
|
||||
}
|
||||
} else { // ["key"] { ... }
|
||||
var objectBody =
|
||||
doVisitObjectBody(
|
||||
objectBodyCtxs,
|
||||
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
language, scope.buildFrameDescriptor(), member, objectBody));
|
||||
}
|
||||
|
||||
return Pair.of(keyNode, member);
|
||||
};
|
||||
return member;
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1350,10 +1332,6 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
result += modifier;
|
||||
}
|
||||
|
||||
if (symbolTable.getCurrentScope().isVisitingIterable()) {
|
||||
result += VmModifier.IS_IN_ITERABLE;
|
||||
}
|
||||
|
||||
// flag modifier combinations that are never valid right away
|
||||
|
||||
if (VmModifier.isExternal(result) && !ModuleKeys.isStdLibModule(moduleKey)) {
|
||||
@@ -1986,7 +1964,6 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
visitArgumentList(argCtx),
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
needsConst,
|
||||
symbolTable.getCurrentScope().isVisitingIterable(),
|
||||
PropagateNullReceiverNodeGen.create(unavailableSourceSection(), receiver),
|
||||
GetClassNodeGen.create(null)));
|
||||
}
|
||||
@@ -1999,7 +1976,6 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
visitArgumentList(argCtx),
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
needsConst,
|
||||
symbolTable.getCurrentScope().isVisitingIterable(),
|
||||
receiver,
|
||||
GetClassNodeGen.create(null));
|
||||
}
|
||||
@@ -2074,11 +2050,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
return InvokeSuperMethodNodeGen.create(
|
||||
sourceSection,
|
||||
memberName,
|
||||
symbolTable.getCurrentScope().isVisitingIterable(),
|
||||
visitArgumentList(argCtx),
|
||||
needsConst);
|
||||
sourceSection, memberName, visitArgumentList(argCtx), needsConst);
|
||||
}
|
||||
|
||||
// superproperty call
|
||||
@@ -2136,8 +2108,7 @@ public final class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
isBaseModule,
|
||||
scope.isCustomThisScope(),
|
||||
scope.getConstLevel(),
|
||||
scope.getConstDepth(),
|
||||
scope.isVisitingIterable());
|
||||
scope.getConstDepth());
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -17,15 +17,14 @@ package org.pkl.core.ast.builder;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor.Builder;
|
||||
import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
import org.pkl.core.TypeParameter;
|
||||
import org.pkl.core.ast.ConstantNode;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.expression.generator.GeneratorMemberNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.parser.Lexer;
|
||||
import org.pkl.core.parser.antlr.PklParser.ParameterContext;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
@@ -35,8 +34,6 @@ import org.pkl.core.util.Nullable;
|
||||
public final class SymbolTable {
|
||||
private Scope currentScope;
|
||||
|
||||
public static Object FOR_GENERATOR_VARIABLE = new Object();
|
||||
|
||||
public SymbolTable(ModuleInfo moduleInfo) {
|
||||
currentScope = new ModuleScope(moduleInfo);
|
||||
}
|
||||
@@ -99,6 +96,19 @@ public final class SymbolTable {
|
||||
nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterForGenerator(
|
||||
FrameDescriptor.Builder frameDescriptorBuilder,
|
||||
FrameDescriptor.Builder memberDescriptorBuilder,
|
||||
Function<ForGeneratorScope, T> nodeFactory) {
|
||||
return doEnter(
|
||||
new ForGeneratorScope(
|
||||
currentScope,
|
||||
currentScope.qualifiedName,
|
||||
frameDescriptorBuilder,
|
||||
memberDescriptorBuilder),
|
||||
nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterLambda(
|
||||
FrameDescriptor.Builder frameDescriptorBuilder, Function<LambdaScope, T> nodeFactory) {
|
||||
|
||||
@@ -128,9 +138,11 @@ public final class SymbolTable {
|
||||
Function<EntryScope, T> nodeFactory) {
|
||||
|
||||
var qualifiedName = currentScope.getQualifiedName() + currentScope.getNextEntryName(keyNode);
|
||||
|
||||
return doEnter(
|
||||
new EntryScope(currentScope, qualifiedName, FrameDescriptor.newBuilder()), nodeFactory);
|
||||
var builder =
|
||||
currentScope instanceof ForGeneratorScope forScope
|
||||
? forScope.memberDescriptorBuilder
|
||||
: FrameDescriptor.newBuilder();
|
||||
return doEnter(new EntryScope(currentScope, qualifiedName, builder), nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterCustomThisScope(Function<CustomThisScope, T> nodeFactory) {
|
||||
@@ -166,12 +178,10 @@ public final class SymbolTable {
|
||||
private final @Nullable Scope parent;
|
||||
private final @Nullable Identifier name;
|
||||
private final String qualifiedName;
|
||||
private final Deque<Identifier> forGeneratorVariables = new ArrayDeque<>();
|
||||
private int lambdaCount = 0;
|
||||
private int entryCount = 0;
|
||||
private final FrameDescriptor.Builder frameDescriptorBuilder;
|
||||
private final ConstLevel constLevel;
|
||||
private boolean isVisitingIterable;
|
||||
|
||||
private Scope(
|
||||
@Nullable Scope parent,
|
||||
@@ -188,7 +198,6 @@ public final class SymbolTable {
|
||||
parent != null && parent.constLevel.biggerOrEquals(constLevel)
|
||||
? parent.constLevel
|
||||
: constLevel;
|
||||
this.isVisitingIterable = parent != null && parent.isVisitingIterable;
|
||||
}
|
||||
|
||||
public final @Nullable Scope getParent() {
|
||||
@@ -212,6 +221,30 @@ public final class SymbolTable {
|
||||
return frameDescriptorBuilder.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new descriptor builder that contains the same slots as the current scope's frame
|
||||
* descriptor.
|
||||
*/
|
||||
public FrameDescriptor.Builder newFrameDescriptorBuilder() {
|
||||
return newFrameDescriptorBuilder(buildFrameDescriptor());
|
||||
}
|
||||
|
||||
/** Returns a new descriptor builder for a {@link GeneratorMemberNode} in the current scope. */
|
||||
public FrameDescriptor.Builder newForGeneratorMemberDescriptorBuilder() {
|
||||
return this instanceof ForGeneratorScope forScope
|
||||
? newFrameDescriptorBuilder(forScope.buildMemberDescriptor())
|
||||
: FrameDescriptor.newBuilder();
|
||||
}
|
||||
|
||||
private static FrameDescriptor.Builder newFrameDescriptorBuilder(FrameDescriptor descriptor) {
|
||||
var builder = FrameDescriptor.newBuilder();
|
||||
for (var i = 0; i < descriptor.getNumberOfSlots(); i++) {
|
||||
builder.addSlot(
|
||||
descriptor.getSlotKind(i), descriptor.getSlotName(i), descriptor.getSlotInfo(i));
|
||||
}
|
||||
return builder;
|
||||
}
|
||||
|
||||
public @Nullable TypeParameter getTypeParameter(String name) {
|
||||
return null;
|
||||
}
|
||||
@@ -253,35 +286,11 @@ public final class SymbolTable {
|
||||
return depth;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds the for generator variable to the frame descriptor.
|
||||
*
|
||||
* <p>Returns {@code -1} if a for-generator variable already exists with this name.
|
||||
*/
|
||||
public int pushForGeneratorVariableContext(ParameterContext ctx) {
|
||||
var variable = Identifier.localProperty(ctx.typedIdentifier().Identifier().getText());
|
||||
if (forGeneratorVariables.contains(variable)) {
|
||||
return -1;
|
||||
}
|
||||
var slot =
|
||||
frameDescriptorBuilder.addSlot(FrameSlotKind.Illegal, variable, FOR_GENERATOR_VARIABLE);
|
||||
forGeneratorVariables.addLast(variable);
|
||||
return slot;
|
||||
}
|
||||
|
||||
public void popForGeneratorVariable() {
|
||||
forGeneratorVariables.removeLast();
|
||||
}
|
||||
|
||||
public Deque<Identifier> getForGeneratorVariables() {
|
||||
return forGeneratorVariables;
|
||||
}
|
||||
|
||||
private String getNextLambdaName() {
|
||||
return "<function#" + (++skipLambdaScopes().lambdaCount) + ">";
|
||||
}
|
||||
|
||||
private String getNextEntryName(@Nullable ExpressionNode keyNode) {
|
||||
protected String getNextEntryName(@Nullable ExpressionNode keyNode) {
|
||||
if (keyNode instanceof ConstantNode constantNode) {
|
||||
var value = constantNode.getValue();
|
||||
if (value instanceof String) {
|
||||
@@ -336,17 +345,13 @@ public final class SymbolTable {
|
||||
return this instanceof LexicalScope;
|
||||
}
|
||||
|
||||
public final boolean isForGeneratorScope() {
|
||||
return this instanceof ForGeneratorScope;
|
||||
}
|
||||
|
||||
public ConstLevel getConstLevel() {
|
||||
return constLevel;
|
||||
}
|
||||
|
||||
public void setVisitingIterable(boolean isVisitingIterable) {
|
||||
this.isVisitingIterable = isVisitingIterable;
|
||||
}
|
||||
|
||||
public boolean isVisitingIterable() {
|
||||
return isVisitingIterable;
|
||||
}
|
||||
}
|
||||
|
||||
private interface LexicalScope {}
|
||||
@@ -413,6 +418,30 @@ public final class SymbolTable {
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ForGeneratorScope extends Scope implements LexicalScope {
|
||||
private final FrameDescriptor.Builder memberDescriptorBuilder;
|
||||
|
||||
public ForGeneratorScope(
|
||||
Scope parent,
|
||||
String qualifiedName,
|
||||
FrameDescriptor.Builder frameDescriptorBuilder,
|
||||
FrameDescriptor.Builder memberDescriptorBuilder) {
|
||||
super(parent, null, qualifiedName, ConstLevel.NONE, frameDescriptorBuilder);
|
||||
this.memberDescriptorBuilder = memberDescriptorBuilder;
|
||||
}
|
||||
|
||||
public FrameDescriptor buildMemberDescriptor() {
|
||||
return memberDescriptorBuilder.build();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected String getNextEntryName(@Nullable ExpressionNode keyNode) {
|
||||
var parent = getParent();
|
||||
assert parent != null;
|
||||
return parent.getNextEntryName(keyNode);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class PropertyScope extends Scope {
|
||||
public PropertyScope(
|
||||
Scope parent,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -21,7 +21,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
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.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.member.FunctionNode;
|
||||
import org.pkl.core.ast.member.UnresolvedFunctionNode;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
@@ -57,7 +56,7 @@ public final class LetExprNode extends ExpressionNode {
|
||||
callNode = insert(DirectCallNode.create(functionNode.getCallTarget()));
|
||||
if (isCustomThisScope) {
|
||||
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
|
||||
customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID);
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,6 +70,6 @@ public final class LetExprNode extends ExpressionNode {
|
||||
|
||||
var value = valueNode.executeGeneric(frame);
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, value);
|
||||
return callNode.call(function.getThisValue(), function, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -19,57 +19,50 @@ import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmDynamic;
|
||||
import org.pkl.core.runtime.VmListing;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
|
||||
@ImportStatic(BaseModule.class)
|
||||
public abstract class GeneratorElementNode extends GeneratorMemberNode {
|
||||
private final ObjectMember element;
|
||||
|
||||
protected GeneratorElementNode(ObjectMember element) {
|
||||
super(element.getSourceSection());
|
||||
protected GeneratorElementNode(ObjectMember element, boolean isFrameStored) {
|
||||
super(element.getSourceSection(), isFrameStored);
|
||||
this.element = element;
|
||||
}
|
||||
|
||||
@Specialization
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalDynamic(VmDynamic parent, ObjectData data) {
|
||||
addElement(data);
|
||||
protected void evalDynamic(VirtualFrame frame, VmDynamic parent, ObjectData data) {
|
||||
data.addElement(frame, element, this);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalListing(VmListing parent, ObjectData data) {
|
||||
addElement(data);
|
||||
protected void evalListing(VirtualFrame frame, VmListing parent, ObjectData data) {
|
||||
data.addElement(frame, element, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "parent == getDynamicClass()")
|
||||
protected void evalDynamicClass(VmClass parent, ObjectData data) {
|
||||
addElement(data);
|
||||
protected void evalDynamicClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addElement(frame, element, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "parent == getListingClass()")
|
||||
protected void evalListingClass(VmClass parent, ObjectData data) {
|
||||
addElement(data);
|
||||
protected void evalListingClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addElement(frame, element, this);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@SuppressWarnings("unused")
|
||||
void fallback(Object parent, ObjectData data) {
|
||||
void fallback(VirtualFrame frame, Object parent, ObjectData data) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder().evalError("objectCannotHaveElement", parent).build();
|
||||
}
|
||||
|
||||
private void addElement(ObjectData data) {
|
||||
long index = data.length;
|
||||
EconomicMaps.put(data.members, index, element);
|
||||
data.length += 1;
|
||||
data.persistForBindings(index);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -25,15 +25,14 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
|
||||
@ImportStatic(BaseModule.class)
|
||||
public abstract class GeneratorEntryNode extends GeneratorMemberNode {
|
||||
@Child private ExpressionNode keyNode;
|
||||
private final ObjectMember member;
|
||||
|
||||
protected GeneratorEntryNode(ExpressionNode keyNode, ObjectMember member) {
|
||||
super(member.getSourceSection());
|
||||
protected GeneratorEntryNode(ExpressionNode keyNode, ObjectMember member, boolean isFrameStored) {
|
||||
super(member.getSourceSection(), isFrameStored);
|
||||
this.keyNode = keyNode;
|
||||
this.member = member;
|
||||
}
|
||||
@@ -84,7 +83,7 @@ public abstract class GeneratorEntryNode extends GeneratorMemberNode {
|
||||
|
||||
private void addRegularEntry(VirtualFrame frame, ObjectData data) {
|
||||
var key = keyNode.executeGeneric(frame);
|
||||
doAdd(key, data);
|
||||
data.addMember(frame, key, member, this);
|
||||
}
|
||||
|
||||
private void addListingEntry(VirtualFrame frame, ObjectData data, int parentLength) {
|
||||
@@ -108,15 +107,6 @@ public abstract class GeneratorEntryNode extends GeneratorMemberNode {
|
||||
.build();
|
||||
}
|
||||
|
||||
doAdd(index, data);
|
||||
}
|
||||
|
||||
private void doAdd(Object key, ObjectData data) {
|
||||
if (EconomicMaps.put(data.members, key, member) != null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw duplicateDefinition(key, member);
|
||||
}
|
||||
|
||||
data.persistForBindings(key);
|
||||
data.addMember(frame, index, member, this);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -16,62 +16,51 @@
|
||||
package org.pkl.core.ast.expression.generator;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.Truffle;
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
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.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.*;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.ast.type.VmTypeMismatchException;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.core.util.Pair;
|
||||
|
||||
public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
private final int keySlot;
|
||||
private final int valueSlot;
|
||||
private final FrameDescriptor generatorDescriptor;
|
||||
@Child private ExpressionNode iterableNode;
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedKeyTypeNode;
|
||||
@Child private @Nullable UnresolvedTypeNode unresolvedValueTypeNode;
|
||||
@Children private final GeneratorMemberNode[] childNodes;
|
||||
@Child private @Nullable TypeNode keyTypeNode;
|
||||
@Child @LateInit private TypeNode valueTypeNode;
|
||||
@Child private @Nullable TypeNode valueTypeNode;
|
||||
|
||||
public GeneratorForNode(
|
||||
SourceSection sourceSection,
|
||||
int keySlot,
|
||||
int valueSlot,
|
||||
FrameDescriptor generatorDescriptor,
|
||||
ExpressionNode iterableNode,
|
||||
// null if for-generator doesn't bind key or `keyTypeNode` is passed instead of this node
|
||||
@Nullable UnresolvedTypeNode unresolvedKeyTypeNode,
|
||||
// null if for-generator doesn't bind value or `valueTypeNode` is passed instead of this node
|
||||
@Nullable UnresolvedTypeNode unresolvedValueTypeNode,
|
||||
// If this node can be constructed at parse time,
|
||||
// it should be passed instead of `unresolvedKeyTypeNode`.
|
||||
GeneratorMemberNode[] childNodes,
|
||||
boolean hasKeyIdentifier,
|
||||
boolean hasValueIdentifier) {
|
||||
|
||||
super(sourceSection);
|
||||
this.keySlot = keySlot;
|
||||
this.valueSlot = valueSlot;
|
||||
@Nullable TypeNode keyTypeNode,
|
||||
// If this node can be constructed at parse time,
|
||||
// it should be passed instead of `unresolvedValueTypeNode`.
|
||||
@Nullable TypeNode valueTypeNode) {
|
||||
super(sourceSection, false);
|
||||
this.generatorDescriptor = generatorDescriptor;
|
||||
this.iterableNode = iterableNode;
|
||||
this.unresolvedKeyTypeNode = unresolvedKeyTypeNode;
|
||||
this.unresolvedValueTypeNode = unresolvedValueTypeNode;
|
||||
this.childNodes = childNodes;
|
||||
|
||||
// initialize now if possible to save later insert()
|
||||
if (unresolvedKeyTypeNode == null && hasKeyIdentifier) {
|
||||
keyTypeNode =
|
||||
new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(keySlot);
|
||||
}
|
||||
if (unresolvedValueTypeNode == null && hasValueIdentifier) {
|
||||
valueTypeNode =
|
||||
new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(valueSlot);
|
||||
}
|
||||
this.keyTypeNode = keyTypeNode;
|
||||
this.valueTypeNode = valueTypeNode;
|
||||
}
|
||||
|
||||
protected abstract void executeWithIterable(
|
||||
@@ -79,6 +68,7 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
|
||||
@Override
|
||||
public final void execute(VirtualFrame frame, Object parent, ObjectData data) {
|
||||
initialize(frame);
|
||||
executeWithIterable(frame, parent, data, iterableNode.executeGeneric(frame));
|
||||
}
|
||||
|
||||
@@ -99,41 +89,33 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmList iterable) {
|
||||
initTypeNodes(frame);
|
||||
long idx = 0;
|
||||
for (Object element : iterable) {
|
||||
executeIteration(frame, parent, data, idx++, element);
|
||||
}
|
||||
resetFrameSlots(frame);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmMap iterable) {
|
||||
initTypeNodes(frame);
|
||||
for (var entry : iterable) {
|
||||
executeIteration(frame, parent, data, VmUtils.getKey(entry), VmUtils.getValue(entry));
|
||||
}
|
||||
resetFrameSlots(frame);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmSet iterable) {
|
||||
initTypeNodes(frame);
|
||||
long idx = 0;
|
||||
for (var element : iterable) {
|
||||
executeIteration(frame, parent, data, idx++, element);
|
||||
}
|
||||
resetFrameSlots(frame);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmMap iterable) {
|
||||
for (var entry : iterable) {
|
||||
executeIteration(frame, parent, data, VmUtils.getKey(entry), VmUtils.getValue(entry));
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmSet iterable) {
|
||||
long idx = 0;
|
||||
for (var element : iterable) {
|
||||
executeIteration(frame, parent, data, idx++, element);
|
||||
}
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmIntSeq iterable) {
|
||||
initTypeNodes(frame);
|
||||
var length = iterable.getLength();
|
||||
for (long key = 0, value = iterable.start; key < length; key++, value += iterable.step) {
|
||||
executeIteration(frame, parent, data, key, value);
|
||||
}
|
||||
resetFrameSlots(frame);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@@ -147,84 +129,55 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
.build();
|
||||
}
|
||||
|
||||
@SuppressWarnings("ForLoopReplaceableByForEach")
|
||||
private void doEvalObject(VirtualFrame frame, VmObject iterable, Object parent, ObjectData data) {
|
||||
initTypeNodes(frame);
|
||||
var members = evaluateMembers(iterable);
|
||||
for (int i = 0; i < members.size(); i++) {
|
||||
var member = members.get(i);
|
||||
executeIteration(frame, parent, data, member.first, member.second);
|
||||
}
|
||||
resetFrameSlots(frame);
|
||||
}
|
||||
|
||||
private void resetFrameSlots(VirtualFrame frame) {
|
||||
if (keySlot != -1) {
|
||||
frame.clear(keySlot);
|
||||
}
|
||||
if (valueSlot != -1) {
|
||||
frame.clear(valueSlot);
|
||||
}
|
||||
}
|
||||
|
||||
private void initTypeNodes(VirtualFrame frame) {
|
||||
if (unresolvedKeyTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
keyTypeNode = insert(unresolvedKeyTypeNode.execute(frame)).initWriteSlotNode(keySlot);
|
||||
unresolvedKeyTypeNode = null;
|
||||
}
|
||||
|
||||
if (unresolvedValueTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
valueTypeNode = insert(unresolvedValueTypeNode.execute(frame)).initWriteSlotNode(valueSlot);
|
||||
unresolvedValueTypeNode = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Evaluate members upfront to make sure that `childNode.execute()` is not behind a Truffle
|
||||
* boundary.
|
||||
*/
|
||||
@TruffleBoundary
|
||||
private List<Pair<Object, Object>> evaluateMembers(VmObject object) {
|
||||
var members = new ArrayList<Pair<Object, Object>>();
|
||||
object.forceAndIterateMemberValues(
|
||||
iterable.forceAndIterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
members.add(Pair.of(member.isProp() ? key.toString() : key, value));
|
||||
var convertedKey = member.isProp() ? key.toString() : key;
|
||||
// TODO: Executing iteration behind a Truffle boundary is bad for performance.
|
||||
// This and similar cases will be fixed in an upcoming PR that replaces method
|
||||
// `(forceAnd)iterateMemberValues` with cursor-based external iterators.
|
||||
executeIteration(frame, parent, data, convertedKey, value);
|
||||
return true;
|
||||
});
|
||||
return members;
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
private void executeIteration(
|
||||
VirtualFrame frame, Object parent, ObjectData data, Object key, Object value) {
|
||||
|
||||
try {
|
||||
if (keyTypeNode != null) {
|
||||
keyTypeNode.executeAndSet(frame, key);
|
||||
}
|
||||
if (valueTypeNode != null) {
|
||||
valueTypeNode.executeAndSet(frame, value);
|
||||
}
|
||||
} catch (VmTypeMismatchException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw e.toVmException();
|
||||
// GraalJS uses the same implementation technique here:
|
||||
// https://github.com/oracle/graaljs/blob/44a11ce6e87/graal-js/src/com.oracle.truffle.js/
|
||||
// src/com/oracle/truffle/js/nodes/function/IterationScopeNode.java#L86-L88
|
||||
var newFrame =
|
||||
Truffle.getRuntime().createVirtualFrame(frame.getArguments(), generatorDescriptor);
|
||||
// the locals in `frame` (if any) are function arguments and/or outer for-generator bindings
|
||||
VmUtils.copyLocals(frame, 0, newFrame, 0, frame.getFrameDescriptor().getNumberOfSlots());
|
||||
if (keyTypeNode != null) {
|
||||
keyTypeNode.executeAndSet(newFrame, key);
|
||||
}
|
||||
|
||||
Object[] prevBindings = null;
|
||||
if (keyTypeNode != null && valueTypeNode != null) {
|
||||
prevBindings = data.addForBinding(key, value);
|
||||
} else if (valueTypeNode != null) {
|
||||
prevBindings = data.addForBinding(value);
|
||||
} else if (keyTypeNode != null) {
|
||||
prevBindings = data.addForBinding(key);
|
||||
if (valueTypeNode != null) {
|
||||
valueTypeNode.executeAndSet(newFrame, value);
|
||||
}
|
||||
|
||||
for (var childNode : childNodes) {
|
||||
childNode.execute(frame, parent, data);
|
||||
//noinspection ForLoopReplaceableByForEach
|
||||
for (int i = 0; i < childNodes.length; i++) {
|
||||
childNodes[i].execute(newFrame, parent, data);
|
||||
}
|
||||
}
|
||||
|
||||
data.resetForBindings(prevBindings);
|
||||
private void initialize(VirtualFrame frame) {
|
||||
if (unresolvedKeyTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var keySlot = frame.getFrameDescriptor().getNumberOfSlots();
|
||||
keyTypeNode = insert(unresolvedKeyTypeNode.execute(frame)).initWriteSlotNode(keySlot);
|
||||
generatorDescriptor.setSlotKind(keySlot, keyTypeNode.getFrameSlotKind());
|
||||
unresolvedKeyTypeNode = null;
|
||||
}
|
||||
if (unresolvedValueTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var valueSlot = frame.getFrameDescriptor().getNumberOfSlots() + (keyTypeNode != null ? 1 : 0);
|
||||
valueTypeNode = insert(unresolvedValueTypeNode.execute(frame)).initWriteSlotNode(valueSlot);
|
||||
generatorDescriptor.setSlotKind(valueSlot, valueTypeNode.getFrameSlotKind());
|
||||
unresolvedValueTypeNode = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -16,25 +16,23 @@
|
||||
package org.pkl.core.ast.expression.generator;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.CompilerDirectives.ValueType;
|
||||
import com.oracle.truffle.api.dsl.Idempotent;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import java.util.Arrays;
|
||||
import org.graalvm.collections.EconomicMap;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmException;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.Nullable;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public abstract class GeneratorMemberNode extends PklNode {
|
||||
protected GeneratorMemberNode(SourceSection sourceSection) {
|
||||
final boolean isFrameStored;
|
||||
|
||||
protected GeneratorMemberNode(SourceSection sourceSection, boolean isFrameStored) {
|
||||
super(sourceSection);
|
||||
this.isFrameStored = isFrameStored;
|
||||
}
|
||||
|
||||
public abstract void execute(VirtualFrame frame, Object parent, ObjectData data);
|
||||
@@ -54,79 +52,39 @@ public abstract class GeneratorMemberNode extends PklNode {
|
||||
}
|
||||
|
||||
@Idempotent
|
||||
protected boolean checkIsValidTypedProperty(VmClass clazz, ObjectMember member) {
|
||||
if (member.isLocal() || clazz.hasProperty(member.getName())) return true;
|
||||
@SuppressWarnings("SameReturnValue")
|
||||
protected final boolean checkIsValidTypedProperty(VmClass clazz, ObjectMember member) {
|
||||
if (member.isLocal()) return true;
|
||||
var memberName = member.getName();
|
||||
var classProperty = clazz.getProperty(memberName);
|
||||
if (classProperty != null && !classProperty.isConstOrFixed()) return true;
|
||||
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
.cannotFindProperty(clazz.getPrototype(), member.getName(), false, false)
|
||||
.withSourceSection(member.getHeaderSection())
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* <code>
|
||||
* x = new Mapping { for (i in IntSeq(1, 3)) for (key, value in Map(4, "Pigeon", 6, "Barn Owl")) [i *
|
||||
* key] = value.reverse() }
|
||||
* </code>
|
||||
*
|
||||
* <p>The above code results in - 1 MemberNode for `value.reverse()` - 1 ObjectMember for `[i *
|
||||
* key] = value.reverse()` - 1 ObjectData.members map with 6 identical ObjectMember values keyed
|
||||
* by `i * key` - 1 ObjectData.forBindings map with 6 distinct arrays keyed by `i * key` Each
|
||||
* array contains three elements, namely the current values for `i`, `key`, and `value`. - 1
|
||||
* VmMapping whose `members` field holds `ObjectData.members` and whose `extraStorage` field holds
|
||||
* `ObjectData.forBindings`. - 3 `FrameSlot`s for `i`, `key`, and `value`
|
||||
*/
|
||||
@ValueType
|
||||
public static final class ObjectData {
|
||||
// member count is exact iff every for/when body has exactly one member
|
||||
ObjectData(int minMemberCount, int length) {
|
||||
this.members = EconomicMaps.create(minMemberCount);
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
final EconomicMap<Object, ObjectMember> members;
|
||||
|
||||
// For-bindings keyed by object member key.
|
||||
// (There is only one ObjectMember instance per lexical member definition,
|
||||
// hence can't store a member's for-bindings there.)
|
||||
final EconomicMap<Object, Object[]> forBindings = EconomicMap.create();
|
||||
|
||||
int length;
|
||||
|
||||
private Object @Nullable [] currentForBindings;
|
||||
|
||||
@TruffleBoundary
|
||||
Object @Nullable [] addForBinding(Object value) {
|
||||
var result = currentForBindings;
|
||||
if (currentForBindings == null) {
|
||||
currentForBindings = new Object[] {value};
|
||||
} else {
|
||||
currentForBindings = Arrays.copyOf(currentForBindings, currentForBindings.length + 1);
|
||||
currentForBindings[currentForBindings.length - 1] = value;
|
||||
if (classProperty == null) {
|
||||
var exception =
|
||||
exceptionBuilder()
|
||||
.cannotFindProperty(clazz.getPrototype(), memberName, false, false)
|
||||
.build();
|
||||
if (member.getHeaderSection().isAvailable()) {
|
||||
exception
|
||||
.getInsertedStackFrames()
|
||||
.put(
|
||||
getRootNode().getCallTarget(),
|
||||
VmUtils.createStackFrame(member.getHeaderSection(), member.getQualifiedName()));
|
||||
}
|
||||
return result;
|
||||
throw exception;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
Object @Nullable [] addForBinding(Object key, Object value) {
|
||||
var result = currentForBindings;
|
||||
if (currentForBindings == null) {
|
||||
currentForBindings = new Object[] {key, value};
|
||||
} else {
|
||||
currentForBindings = Arrays.copyOf(currentForBindings, currentForBindings.length + 2);
|
||||
currentForBindings[currentForBindings.length - 2] = key;
|
||||
currentForBindings[currentForBindings.length - 1] = value;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
void persistForBindings(Object key) {
|
||||
EconomicMaps.put(forBindings, key, currentForBindings);
|
||||
}
|
||||
|
||||
void resetForBindings(Object @Nullable [] bindings) {
|
||||
currentForBindings = bindings;
|
||||
assert classProperty.isConstOrFixed();
|
||||
var errMsg =
|
||||
classProperty.isConst() ? "cannotAssignConstProperty" : "cannotAssignFixedProperty";
|
||||
var exception = exceptionBuilder().evalError(errMsg, memberName).build();
|
||||
if (member.getHeaderSection().isAvailable()) {
|
||||
exception
|
||||
.getInsertedStackFrames()
|
||||
.put(
|
||||
getRootNode().getCallTarget(),
|
||||
VmUtils.createStackFrame(member.getHeaderSection(), member.getQualifiedName()));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -27,7 +27,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
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.expression.generator.GeneratorMemberNode.ObjectData;
|
||||
import org.pkl.core.ast.expression.literal.AmendFunctionNode;
|
||||
import org.pkl.core.ast.expression.literal.ObjectLiteralNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
@@ -73,34 +72,31 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
||||
|
||||
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
||||
protected VmDynamic evalDynamic(VirtualFrame frame, VmDynamic parent) {
|
||||
var data = createData(frame, parent, parent.getLength());
|
||||
var result = new VmDynamic(frame.materialize(), parent, data.members, data.length);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
var data = executeChildren(frame, parent, parent.getLength());
|
||||
var result = new VmDynamic(frame.materialize(), parent, data.members(), data.length());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
||||
protected VmTyped evalTyped(VirtualFrame frame, VmTyped parent) {
|
||||
VmUtils.checkIsInstantiable(parent.getVmClass(), getParentNode());
|
||||
var data = createData(frame, parent, 0);
|
||||
assert data.forBindings.isEmpty();
|
||||
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), data.members);
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
assert data.hasNoGeneratorFrames();
|
||||
return new VmTyped(frame.materialize(), parent, parent.getVmClass(), data.members());
|
||||
}
|
||||
|
||||
@Specialization(guards = "checkListingCannotHaveParameters()")
|
||||
protected VmListing evalListing(VirtualFrame frame, VmListing parent) {
|
||||
var data = createData(frame, parent, parent.getLength());
|
||||
var result = new VmListing(frame.materialize(), parent, data.members, data.length);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
var data = executeChildren(frame, parent, parent.getLength());
|
||||
var result = new VmListing(frame.materialize(), parent, data.members(), data.length());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = "checkMappingCannotHaveParameters()")
|
||||
protected VmMapping evalMapping(VirtualFrame frame, VmMapping parent) {
|
||||
var data = createData(frame, parent, 0);
|
||||
var result = new VmMapping(frame.materialize(), parent, data.members);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
var result = new VmMapping(frame.materialize(), parent, data.members());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = "checkObjectCannotHaveParameters()")
|
||||
@@ -110,7 +106,7 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
||||
}
|
||||
|
||||
@Specialization(guards = "checkIsValidFunctionAmendment(parent)")
|
||||
protected Object evalFunction(
|
||||
protected VmFunction evalFunction(
|
||||
VirtualFrame frame,
|
||||
VmFunction parent,
|
||||
@Cached(value = "createAmendFunctionNode(frame)", neverDefault = true)
|
||||
@@ -120,41 +116,34 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getDynamicClass()", "checkObjectCannotHaveParameters()"})
|
||||
protected VmDynamic evalDynamicClass(
|
||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||
var data = createData(frame, parent, 0);
|
||||
protected VmDynamic evalDynamicClass(VirtualFrame frame, VmClass parent) {
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
var result =
|
||||
new VmDynamic(frame.materialize(), parent.getPrototype(), data.members, data.length);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
new VmDynamic(frame.materialize(), parent.getPrototype(), data.members(), data.length());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getMappingClass()", "checkMappingCannotHaveParameters()"})
|
||||
protected VmMapping evalMappingClass(
|
||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||
var data = createData(frame, parent, 0);
|
||||
var result = new VmMapping(frame.materialize(), parent.getPrototype(), data.members);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
protected VmMapping evalMappingClass(VirtualFrame frame, VmClass parent) {
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
var result = new VmMapping(frame.materialize(), parent.getPrototype(), data.members());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getListingClass()", "checkListingCannotHaveParameters()"})
|
||||
protected VmListing evalListingClass(
|
||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||
var data = createData(frame, parent, 0);
|
||||
protected VmListing evalListingClass(VirtualFrame frame, VmClass parent) {
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
var result =
|
||||
new VmListing(frame.materialize(), parent.getPrototype(), data.members, data.length);
|
||||
result.setExtraStorage(data.forBindings);
|
||||
return result;
|
||||
new VmListing(frame.materialize(), parent.getPrototype(), data.members(), data.length());
|
||||
return data.storeGeneratorFrames(result);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"isTypedObjectClass(parent)", "checkObjectCannotHaveParameters()"})
|
||||
protected VmTyped evalTypedObjectClass(
|
||||
VirtualFrame frame, @SuppressWarnings("unused") VmClass parent) {
|
||||
protected VmTyped evalTypedObjectClass(VirtualFrame frame, VmClass parent) {
|
||||
VmUtils.checkIsInstantiable(parent, getParentNode());
|
||||
var data = createData(frame, parent, 0);
|
||||
assert data.forBindings.isEmpty();
|
||||
return new VmTyped(frame.materialize(), parent.getPrototype(), parent, data.members);
|
||||
var data = executeChildren(frame, parent, 0);
|
||||
assert data.hasNoGeneratorFrames();
|
||||
return new VmTyped(frame.materialize(), parent.getPrototype(), parent, data.members());
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@@ -200,9 +189,9 @@ public abstract class GeneratorObjectLiteralNode extends ObjectLiteralNode {
|
||||
}
|
||||
|
||||
@ExplodeLoop
|
||||
private ObjectData createData(VirtualFrame frame, Object parent, int parentLength) {
|
||||
var data = new ObjectData(memberNodes.length, parentLength);
|
||||
for (GeneratorMemberNode memberNode : memberNodes) {
|
||||
private ObjectData executeChildren(VirtualFrame frame, Object parent, int parentLength) {
|
||||
var data = new ObjectData(parentLength);
|
||||
for (var memberNode : memberNodes) {
|
||||
memberNode.execute(frame, parent, data);
|
||||
}
|
||||
return data;
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -34,8 +34,9 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
||||
|
||||
@CompilationFinal private int customThisSlot = -1;
|
||||
|
||||
protected GeneratorPredicateMemberNode(ExpressionNode predicateNode, ObjectMember member) {
|
||||
super(member.getSourceSection());
|
||||
protected GeneratorPredicateMemberNode(
|
||||
ExpressionNode predicateNode, ObjectMember member, boolean isFrameStored) {
|
||||
super(member.getSourceSection(), isFrameStored);
|
||||
this.predicateNode = predicateNode;
|
||||
this.member = member;
|
||||
}
|
||||
@@ -110,7 +111,7 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
||||
|
||||
try {
|
||||
var isApplicable = predicateNode.executeBoolean(frame);
|
||||
if (isApplicable) doAdd(key, data);
|
||||
if (isApplicable) data.addMember(frame, key, this.member, this);
|
||||
} catch (UnexpectedResultException e) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
@@ -134,13 +135,4 @@ public abstract class GeneratorPredicateMemberNode extends GeneratorMemberNode {
|
||||
frame.getFrameDescriptor().findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
}
|
||||
}
|
||||
|
||||
private void doAdd(Object key, ObjectData data) {
|
||||
if (EconomicMaps.put(data.members, key, member) != null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw duplicateDefinition(key, member);
|
||||
}
|
||||
|
||||
data.persistForBindings(key);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -20,67 +20,67 @@ import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.Idempotent;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
|
||||
@ImportStatic({BaseModule.class, GeneratorObjectLiteralNode.class})
|
||||
public abstract class GeneratorPropertyNode extends GeneratorMemberNode {
|
||||
protected final ObjectMember member;
|
||||
|
||||
protected GeneratorPropertyNode(ObjectMember member) {
|
||||
super(member.getSourceSection());
|
||||
super(member.getSourceSection(), false);
|
||||
this.member = member;
|
||||
assert member.isProp();
|
||||
}
|
||||
|
||||
@Specialization
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalDynamic(VmDynamic parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalDynamic(VirtualFrame frame, VmDynamic parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "checkIsValidTypedProperty(parent.getVmClass(), member)")
|
||||
protected void evalTyped(VmTyped parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalTyped(VirtualFrame frame, VmTyped parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "checkIsValidMappingProperty()")
|
||||
protected void evalMapping(VmMapping parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalMapping(VirtualFrame frame, VmMapping parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "checkIsValidListingProperty()")
|
||||
protected void evalListing(VmListing parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalListing(VirtualFrame frame, VmListing parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = "parent == getDynamicClass()")
|
||||
protected void evalDynamicClass(VmClass parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalDynamicClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = {"parent == getMappingClass()", "checkIsValidMappingProperty()"})
|
||||
protected void evalMappingClass(VmClass parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalMappingClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(guards = {"parent == getListingClass()", "checkIsValidListingProperty()"})
|
||||
protected void evalListingClass(VmClass parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalListingClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
@Specialization(
|
||||
guards = {"isTypedObjectClass(parent)", "checkIsValidTypedProperty(parent, member)"})
|
||||
protected void evalTypedObjectClass(VmClass parent, ObjectData data) {
|
||||
addProperty(data);
|
||||
protected void evalTypedObjectClass(VirtualFrame frame, VmClass parent, ObjectData data) {
|
||||
data.addProperty(frame, member, this);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@@ -116,11 +116,4 @@ public abstract class GeneratorPropertyNode extends GeneratorMemberNode {
|
||||
.withSourceSection(member.getHeaderSection())
|
||||
.build();
|
||||
}
|
||||
|
||||
private void addProperty(ObjectData data) {
|
||||
if (EconomicMaps.put(data.members, member.getName(), member) == null) return;
|
||||
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw duplicateDefinition(member.getName(), member);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -19,7 +19,6 @@ import static org.pkl.core.runtime.BaseModule.getListingClass;
|
||||
import static org.pkl.core.runtime.BaseModule.getMappingClass;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary;
|
||||
import com.oracle.truffle.api.dsl.Fallback;
|
||||
import com.oracle.truffle.api.dsl.ImportStatic;
|
||||
import com.oracle.truffle.api.dsl.Specialization;
|
||||
@@ -29,9 +28,11 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.Iterators.TruffleIterator;
|
||||
import org.pkl.core.runtime.VmClass;
|
||||
import org.pkl.core.runtime.VmCollection;
|
||||
import org.pkl.core.runtime.VmDynamic;
|
||||
import org.pkl.core.runtime.VmException;
|
||||
import org.pkl.core.runtime.VmException.ProgramValue;
|
||||
import org.pkl.core.runtime.VmIntSeq;
|
||||
import org.pkl.core.runtime.VmListing;
|
||||
@@ -41,8 +42,6 @@ import org.pkl.core.runtime.VmNull;
|
||||
import org.pkl.core.runtime.VmObject;
|
||||
import org.pkl.core.runtime.VmTyped;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
import org.pkl.core.util.MutableLong;
|
||||
|
||||
@ImportStatic(BaseModule.class)
|
||||
public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
@@ -51,7 +50,7 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
|
||||
public GeneratorSpreadNode(
|
||||
SourceSection sourceSection, ExpressionNode iterableNode, boolean nullable) {
|
||||
super(sourceSection);
|
||||
super(sourceSection, false);
|
||||
this.iterableNode = iterableNode;
|
||||
this.nullable = nullable;
|
||||
}
|
||||
@@ -84,78 +83,82 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
|
||||
@Specialization(guards = "!iterable.isTyped()")
|
||||
@SuppressWarnings("unused")
|
||||
protected void eval(VmDynamic parent, ObjectData data, VmObject iterable) {
|
||||
doEvalDynamic(data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmDynamic parent, ObjectData data, VmObject iterable) {
|
||||
doEvalDynamic(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = "!iterable.isTyped()")
|
||||
@SuppressWarnings("unused")
|
||||
protected void eval(VmListing parent, ObjectData data, VmObject iterable) {
|
||||
doEvalListing(data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmListing parent, ObjectData data, VmObject iterable) {
|
||||
doEvalListing(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = "!iterable.isTyped()")
|
||||
@SuppressWarnings("unused")
|
||||
protected void eval(VmMapping parent, ObjectData data, VmObject iterable) {
|
||||
doEvalMapping(data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmMapping parent, ObjectData data, VmObject iterable) {
|
||||
doEvalMapping(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getDynamicClass()", "!iterable.isTyped()"})
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalDynamicClass(VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalDynamic(data, iterable);
|
||||
protected void evalDynamicClass(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalDynamic(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getListingClass()", "!iterable.isTyped()"})
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalListingClass(VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalListing(data, iterable);
|
||||
protected void evalListingClass(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalListing(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"parent == getMappingClass()", "!iterable.isTyped()"})
|
||||
@SuppressWarnings("unused")
|
||||
protected void evalMappingClass(VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalMapping(data, iterable);
|
||||
protected void evalMappingClass(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalMapping(frame, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"isTypedObjectClass(parent)", "!iterable.isTyped()"})
|
||||
protected void evalTypedClass(VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalTyped(parent, data, iterable);
|
||||
protected void evalTypedClass(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmObject iterable) {
|
||||
doEvalTyped(frame, parent, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization(guards = {"!iterable.isTyped()"})
|
||||
protected void eval(VmTyped parent, ObjectData data, VmObject iterable) {
|
||||
doEvalTyped(parent.getVmClass(), data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmTyped parent, ObjectData data, VmObject iterable) {
|
||||
doEvalTyped(frame, parent.getVmClass(), data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmObject parent, ObjectData data, VmMap iterable) {
|
||||
doEvalMap(parent.getVmClass(), data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmObject parent, ObjectData data, VmMap iterable) {
|
||||
doEvalMap(frame, parent.getVmClass(), data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmClass parent, ObjectData data, VmMap iterable) {
|
||||
doEvalMap(parent, data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmClass parent, ObjectData data, VmMap iterable) {
|
||||
doEvalMap(frame, parent, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmObject parent, ObjectData data, VmCollection iterable) {
|
||||
doEvalCollection(parent.getVmClass(), data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmObject parent, ObjectData data, VmCollection iterable) {
|
||||
doEvalCollection(frame, parent.getVmClass(), data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmClass parent, ObjectData data, VmCollection iterable) {
|
||||
doEvalCollection(parent, data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmClass parent, ObjectData data, VmCollection iterable) {
|
||||
doEvalCollection(frame, parent, data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmObject parent, ObjectData data, VmIntSeq iterable) {
|
||||
doEvalIntSeq(parent.getVmClass(), data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmObject parent, ObjectData data, VmIntSeq iterable) {
|
||||
doEvalIntSeq(frame, parent.getVmClass(), data, iterable);
|
||||
}
|
||||
|
||||
@Specialization
|
||||
protected void eval(VmClass parent, ObjectData data, VmIntSeq iterable) {
|
||||
doEvalIntSeq(parent, data, iterable);
|
||||
protected void eval(VirtualFrame frame, VmClass parent, ObjectData data, VmIntSeq iterable) {
|
||||
doEvalIntSeq(frame, parent, data, iterable);
|
||||
}
|
||||
|
||||
@Fallback
|
||||
@@ -174,64 +177,55 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
throw builder.build();
|
||||
}
|
||||
|
||||
protected void doEvalDynamic(ObjectData data, VmObject iterable) {
|
||||
var length = new MutableLong(data.length);
|
||||
protected void doEvalDynamic(VirtualFrame frame, ObjectData data, VmObject iterable) {
|
||||
iterable.forceAndIterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
if (member.isElement()) {
|
||||
EconomicMaps.put(data.members, length.getAndIncrement(), createMember(member, value));
|
||||
data.addElement(frame, createMember(member, value), this);
|
||||
} else {
|
||||
if (EconomicMaps.put(data.members, key, createMember(member, value)) != null) {
|
||||
duplicateMember(key, member);
|
||||
}
|
||||
data.addMember(frame, key, createMember(member, value), this);
|
||||
}
|
||||
return true;
|
||||
});
|
||||
data.length = (int) length.get();
|
||||
}
|
||||
|
||||
private void doEvalMapping(ObjectData data, VmObject iterable) {
|
||||
private void doEvalMapping(VirtualFrame frame, ObjectData data, VmObject iterable) {
|
||||
iterable.forceAndIterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
if (member.isElement() || member.isProp()) {
|
||||
cannotHaveMember(BaseModule.getMappingClass(), member);
|
||||
}
|
||||
if (EconomicMaps.put(data.members, key, createMember(member, value)) != null) {
|
||||
duplicateMember(key, member);
|
||||
}
|
||||
data.addMember(frame, key, createMember(member, value), this);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
private void doEvalListing(ObjectData data, VmObject iterable) {
|
||||
var length = new MutableLong(data.length);
|
||||
private void doEvalListing(VirtualFrame frame, ObjectData data, VmObject iterable) {
|
||||
iterable.forceAndIterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
if (member.isEntry() || member.isProp()) {
|
||||
cannotHaveMember(getListingClass(), member);
|
||||
}
|
||||
EconomicMaps.put(data.members, length.getAndIncrement(), createMember(member, value));
|
||||
data.addElement(frame, createMember(member, value), this);
|
||||
return true;
|
||||
});
|
||||
data.length = (int) length.get();
|
||||
}
|
||||
|
||||
private void doEvalTyped(VmClass clazz, ObjectData data, VmObject iterable) {
|
||||
private void doEvalTyped(VirtualFrame frame, VmClass clazz, ObjectData data, VmObject iterable) {
|
||||
iterable.forceAndIterateMemberValues(
|
||||
(key, member, value) -> {
|
||||
if (member.isElement() || member.isEntry()) {
|
||||
cannotHaveMember(clazz, member);
|
||||
}
|
||||
checkTypedProperty(clazz, member);
|
||||
if (EconomicMaps.put(data.members, key, createMember(member, value)) != null) {
|
||||
duplicateMember(key, member);
|
||||
}
|
||||
checkIsValidTypedProperty(clazz, member);
|
||||
data.addProperty(frame, createMember(member, value), this);
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
// handles both `List` and `Set`
|
||||
private void doEvalCollection(VmClass parent, ObjectData data, VmCollection iterable) {
|
||||
private void doEvalCollection(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmCollection iterable) {
|
||||
if (isTypedObjectClass(parent) || parent == getMappingClass()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
@@ -241,10 +235,10 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
.withProgramValue("Value", iterable)
|
||||
.build();
|
||||
}
|
||||
spreadIterable(data, iterable);
|
||||
spreadIterable(frame, data, iterable);
|
||||
}
|
||||
|
||||
private void doEvalMap(VmClass parent, ObjectData data, VmMap iterable) {
|
||||
private void doEvalMap(VirtualFrame frame, VmClass parent, ObjectData data, VmMap iterable) {
|
||||
if (isTypedObjectClass(parent) || parent == getListingClass()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
@@ -255,13 +249,12 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
}
|
||||
for (var entry : iterable) {
|
||||
var member = VmUtils.createSyntheticObjectEntry("", VmUtils.getValue(entry));
|
||||
if (EconomicMaps.put(data.members, VmUtils.getKey(entry), member) != null) {
|
||||
duplicateMember(VmUtils.getKey(entry), member);
|
||||
}
|
||||
data.addMember(frame, VmUtils.getKey(entry), member, this);
|
||||
}
|
||||
}
|
||||
|
||||
private void doEvalIntSeq(VmClass parent, ObjectData data, VmIntSeq iterable) {
|
||||
private void doEvalIntSeq(
|
||||
VirtualFrame frame, VmClass parent, ObjectData data, VmIntSeq iterable) {
|
||||
if (isTypedObjectClass(parent) || parent == getMappingClass()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
@@ -270,7 +263,7 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
.withProgramValue("Value", iterable)
|
||||
.build();
|
||||
}
|
||||
spreadIterable(data, iterable);
|
||||
spreadIterable(frame, data, iterable);
|
||||
}
|
||||
|
||||
private void cannotHaveMember(VmClass clazz, ObjectMember member) {
|
||||
@@ -295,7 +288,8 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
throw exception;
|
||||
}
|
||||
|
||||
private void duplicateMember(Object key, ObjectMember member) {
|
||||
@Override
|
||||
protected VmException duplicateDefinition(Object key, ObjectMember member) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
var exception =
|
||||
exceptionBuilder()
|
||||
@@ -331,51 +325,12 @@ public abstract class GeneratorSpreadNode extends GeneratorMemberNode {
|
||||
return result;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
private void spreadIterable(ObjectData data, Iterable<?> iterable) {
|
||||
var length = data.length;
|
||||
for (var elem : iterable) {
|
||||
var index = length++;
|
||||
var member = VmUtils.createSyntheticObjectElement(String.valueOf(index), elem);
|
||||
EconomicMaps.put(data.members, (long) index, member);
|
||||
}
|
||||
data.length = length;
|
||||
}
|
||||
|
||||
protected void checkTypedProperty(VmClass clazz, ObjectMember member) {
|
||||
if (member.isLocal()) return;
|
||||
|
||||
var memberName = member.getName();
|
||||
var classProperty = clazz.getProperty(memberName);
|
||||
if (classProperty == null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
var exception =
|
||||
exceptionBuilder()
|
||||
.cannotFindProperty(clazz.getPrototype(), memberName, false, false)
|
||||
.build();
|
||||
if (member.getHeaderSection().isAvailable()) {
|
||||
exception
|
||||
.getInsertedStackFrames()
|
||||
.put(
|
||||
getRootNode().getCallTarget(),
|
||||
VmUtils.createStackFrame(member.getHeaderSection(), member.getQualifiedName()));
|
||||
}
|
||||
throw exception;
|
||||
}
|
||||
|
||||
if (classProperty.isConstOrFixed()) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
var errMsg =
|
||||
classProperty.isConst() ? "cannotAssignConstProperty" : "cannotAssignFixedProperty";
|
||||
var exception = exceptionBuilder().evalError(errMsg, memberName).build();
|
||||
if (member.getHeaderSection().isAvailable()) {
|
||||
exception
|
||||
.getInsertedStackFrames()
|
||||
.put(
|
||||
getRootNode().getCallTarget(),
|
||||
VmUtils.createStackFrame(member.getHeaderSection(), member.getQualifiedName()));
|
||||
}
|
||||
throw exception;
|
||||
private void spreadIterable(VirtualFrame frame, ObjectData data, Iterable<?> iterable) {
|
||||
var iterator = new TruffleIterator<>(iterable);
|
||||
while (iterator.hasNext()) {
|
||||
var elem = iterator.next();
|
||||
var member = VmUtils.createSyntheticObjectElement(String.valueOf(data.length()), elem);
|
||||
data.addElement(frame, member, this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -34,7 +34,7 @@ public final class GeneratorWhenNode extends GeneratorMemberNode {
|
||||
GeneratorMemberNode[] thenNodes,
|
||||
GeneratorMemberNode[] elseNodes) {
|
||||
|
||||
super(sourceSection);
|
||||
super(sourceSection, false);
|
||||
this.conditionNode = conditionNode;
|
||||
this.thenNodes = thenNodes;
|
||||
this.elseNodes = elseNodes;
|
||||
|
||||
@@ -0,0 +1,93 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 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.generator;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.graalvm.collections.EconomicMap;
|
||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.VmObject;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
|
||||
/** Data collected by {@link GeneratorObjectLiteralNode} to generate a `VmObject`. */
|
||||
public final class ObjectData {
|
||||
/** The object's members. */
|
||||
private final EconomicMap<Object, ObjectMember> members;
|
||||
|
||||
/**
|
||||
* The frames that were active when `members` were generated. Only a subset of members have their
|
||||
* frames stored ({@link GeneratorMemberNode#isFrameStored}). Frames are stored in
|
||||
* `owner.extraStorage` and retrieved by `RestoreForBindingsNode` when members are executed
|
||||
*/
|
||||
private final EconomicMap<Object, MaterializedFrame> generatorFrames;
|
||||
|
||||
/** The object's number of elements. */
|
||||
private int length;
|
||||
|
||||
ObjectData(int parentLength) {
|
||||
// optimize for memory usage by not estimating minimum size
|
||||
members = EconomicMaps.create();
|
||||
generatorFrames = EconomicMaps.create();
|
||||
length = parentLength;
|
||||
}
|
||||
|
||||
UnmodifiableEconomicMap<Object, ObjectMember> members() {
|
||||
return members;
|
||||
}
|
||||
|
||||
int length() {
|
||||
return length;
|
||||
}
|
||||
|
||||
boolean hasNoGeneratorFrames() {
|
||||
return generatorFrames.isEmpty();
|
||||
}
|
||||
|
||||
void addElement(VirtualFrame frame, ObjectMember member, GeneratorMemberNode node) {
|
||||
addMember(frame, (long) length, member, node);
|
||||
length += 1;
|
||||
}
|
||||
|
||||
void addProperty(VirtualFrame frame, ObjectMember member, GeneratorMemberNode node) {
|
||||
addMember(frame, member.getName(), member, node);
|
||||
}
|
||||
|
||||
void addMember(VirtualFrame frame, Object key, ObjectMember member, GeneratorMemberNode node) {
|
||||
if (EconomicMaps.put(members, key, member) != null) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw node.duplicateDefinition(key, member);
|
||||
}
|
||||
if (node.isFrameStored) {
|
||||
EconomicMaps.put(generatorFrames, key, frame.materialize());
|
||||
}
|
||||
}
|
||||
|
||||
<T extends VmObject> T storeGeneratorFrames(T object) {
|
||||
object.setExtraStorage(generatorFrames);
|
||||
return object;
|
||||
}
|
||||
|
||||
static MaterializedFrame getGeneratorFrame(VirtualFrame frame) {
|
||||
@SuppressWarnings("unchecked")
|
||||
var map = (EconomicMap<Object, MaterializedFrame>) VmUtils.getOwner(frame).getExtraStorage();
|
||||
var result = EconomicMaps.get(map, VmUtils.getMemberKey(frame));
|
||||
assert result != null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
/*
|
||||
* Copyright © 2024-2025 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.generator;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
/**
|
||||
* Restores for-generator variable bindings when a member generated by a for-generator is executed.
|
||||
*/
|
||||
public final class RestoreForBindingsNode extends ExpressionNode {
|
||||
private @Child ExpressionNode child;
|
||||
|
||||
public RestoreForBindingsNode(ExpressionNode child) {
|
||||
super(child.getSourceSection());
|
||||
this.child = child;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var generatorFrame = ObjectData.getGeneratorFrame(frame);
|
||||
var numSlots = frame.getFrameDescriptor().getNumberOfSlots();
|
||||
// This value is constant and could be a constructor argument.
|
||||
var startSlot = generatorFrame.getFrameDescriptor().getNumberOfSlots() - numSlots;
|
||||
assert startSlot >= 0;
|
||||
// Copy locals that are for-generator variables into this frame.
|
||||
// Slots before `startSlot` (if any) are function arguments
|
||||
// and must not be copied to preserve scoping rules.
|
||||
VmUtils.copyLocals(generatorFrame, startSlot, frame, 0, numSlots);
|
||||
return child.executeGeneric(frame);
|
||||
}
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024 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.generator;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import org.graalvm.collections.UnmodifiableEconomicMap;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.EconomicMaps;
|
||||
|
||||
public final class WriteForVariablesNode extends ExpressionNode {
|
||||
private final int[] auxiliarySlots;
|
||||
@Child private ExpressionNode childNode;
|
||||
|
||||
public WriteForVariablesNode(int[] auxiliarySlots, ExpressionNode childNode) {
|
||||
this.auxiliarySlots = auxiliarySlots;
|
||||
this.childNode = childNode;
|
||||
}
|
||||
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var extraStorage = VmUtils.getOwner(frame).getExtraStorage();
|
||||
assert extraStorage instanceof UnmodifiableEconomicMap;
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
var forBindings = (UnmodifiableEconomicMap<Object, Object[]>) extraStorage;
|
||||
var bindings = EconomicMaps.get(forBindings, VmUtils.getMemberKey(frame));
|
||||
assert bindings != null;
|
||||
assert bindings.length == auxiliarySlots.length;
|
||||
|
||||
for (var i = 0; i < auxiliarySlots.length; i++) {
|
||||
frame.setAuxiliarySlot(auxiliarySlots[i], bindings[i]);
|
||||
}
|
||||
|
||||
return childNode.executeGeneric(frame);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -26,8 +26,6 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.PklRootNode;
|
||||
import org.pkl.core.ast.SimpleRootNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.frame.ReadFrameSlotNodeGen;
|
||||
import org.pkl.core.ast.member.FunctionNode;
|
||||
import org.pkl.core.ast.member.Lambda;
|
||||
@@ -40,10 +38,7 @@ public final class AmendFunctionNode extends PklNode {
|
||||
private final PklRootNode initialFunctionRootNode;
|
||||
@CompilationFinal private int customThisSlot = -1;
|
||||
|
||||
public AmendFunctionNode(
|
||||
ObjectLiteralNode hostNode,
|
||||
TypeNode[] parameterTypeNodes,
|
||||
FrameDescriptor hostFrameDescriptor) {
|
||||
public AmendFunctionNode(ObjectLiteralNode hostNode, TypeNode[] parameterTypeNodes) {
|
||||
super(hostNode.getSourceSection());
|
||||
|
||||
isCustomThisScope = hostNode.isCustomThisScope;
|
||||
@@ -61,39 +56,7 @@ public final class AmendFunctionNode extends PklNode {
|
||||
} else {
|
||||
parameterSlots = new int[0];
|
||||
}
|
||||
var hasForGenVars = false;
|
||||
for (var i = 0; i < hostFrameDescriptor.getNumberOfSlots(); i++) {
|
||||
var slotInfo = hostFrameDescriptor.getSlotInfo(i);
|
||||
// Copy for-generator variables from the outer frame descriptor into inner lambda.
|
||||
//
|
||||
// We need to do this because at parse time within AstBuilder, we inject for-generator
|
||||
// variables into the frame descriptor of the containing root node.
|
||||
// The expectation is that when GeneratorForNode executes, it writes for-generator variables
|
||||
// into these slots.
|
||||
//
|
||||
// In the case of an amend function node, AstBuilder can't determine out that there is another
|
||||
// frame (e.g. with `new Mixin { ... }` syntax), so it injects for-generator vars into the
|
||||
// wrong frame.
|
||||
//
|
||||
// As a remedy, we simply copy outer variables into this frame if there are any for generator
|
||||
// variables.
|
||||
//
|
||||
// We need to preserve the frame slot index, so we insert dummy identifiers
|
||||
// for other slots that aren't for generator variables.
|
||||
if (slotInfo != null && slotInfo.equals(SymbolTable.FOR_GENERATOR_VARIABLE)) {
|
||||
if (!hasForGenVars) {
|
||||
hasForGenVars = true;
|
||||
for (var j = 0; j < i; j++) {
|
||||
builder.addSlot(FrameSlotKind.Illegal, Identifier.DUMMY, null);
|
||||
}
|
||||
}
|
||||
builder.addSlot(
|
||||
hostFrameDescriptor.getSlotKind(i), hostFrameDescriptor.getSlotName(i), null);
|
||||
} else if (hasForGenVars) {
|
||||
builder.addSlot(FrameSlotKind.Illegal, Identifier.DUMMY, null);
|
||||
}
|
||||
}
|
||||
var objectToAmendSlot = builder.addSlot(FrameSlotKind.Object, new Object(), null);
|
||||
var objectToAmendSlot = builder.addSlot(FrameSlotKind.Object, null, null);
|
||||
var frameDescriptor = builder.build();
|
||||
|
||||
var subsequentFunctionRootNode =
|
||||
@@ -138,7 +101,7 @@ public final class AmendFunctionNode extends PklNode {
|
||||
public VmFunction execute(VirtualFrame frame, VmFunction functionToAmend) {
|
||||
if (isCustomThisScope && customThisSlot == -1) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID);
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
return new VmFunction(
|
||||
frame.materialize(),
|
||||
@@ -184,8 +147,7 @@ public final class AmendFunctionNode extends PklNode {
|
||||
var arguments = new Object[frameArguments.length];
|
||||
arguments[0] = functionToAmend.getThisValue();
|
||||
arguments[1] = functionToAmend;
|
||||
arguments[2] = false;
|
||||
System.arraycopy(frameArguments, 3, arguments, 3, frameArguments.length - 3);
|
||||
System.arraycopy(frameArguments, 2, arguments, 2, frameArguments.length - 2);
|
||||
|
||||
var valueToAmend = callNode.call(functionToAmend.getCallTarget(), arguments);
|
||||
if (!(valueToAmend instanceof VmFunction newFunctionToAmend)) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -20,7 +20,6 @@ import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.member.FunctionNode;
|
||||
import org.pkl.core.ast.member.UnresolvedFunctionNode;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
@@ -48,7 +47,7 @@ public final class FunctionLiteralNode extends ExpressionNode {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
functionNode = unresolvedFunctionNode.execute(frame);
|
||||
if (isCustomThisScope) {
|
||||
customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID);
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -66,7 +66,7 @@ public abstract class ObjectLiteralNode extends ExpressionNode {
|
||||
parametersDescriptor == null
|
||||
? new TypeNode[0]
|
||||
: VmUtils.resolveParameterTypes(frame, parametersDescriptor, parameterTypes);
|
||||
return new AmendFunctionNode(this, resolvedParameterTypes, frame.getFrameDescriptor());
|
||||
return new AmendFunctionNode(this, resolvedParameterTypes);
|
||||
}
|
||||
|
||||
@Idempotent
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -28,7 +28,6 @@ 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;
|
||||
|
||||
@@ -36,14 +35,12 @@ public final class InvokeMethodDirectNode extends ExpressionNode {
|
||||
SourceSection sourceSection,
|
||||
ClassMethod method,
|
||||
ExpressionNode receiverNode,
|
||||
ExpressionNode[] argumentNodes,
|
||||
boolean isInIterable) {
|
||||
ExpressionNode[] argumentNodes) {
|
||||
|
||||
super(sourceSection);
|
||||
this.owner = method.getOwner();
|
||||
this.receiverNode = receiverNode;
|
||||
this.argumentNodes = argumentNodes;
|
||||
this.isInIterable = isInIterable;
|
||||
|
||||
callNode = DirectCallNode.create(method.getCallTarget(sourceSection));
|
||||
}
|
||||
@@ -51,12 +48,11 @@ public final class InvokeMethodDirectNode extends ExpressionNode {
|
||||
@Override
|
||||
@ExplodeLoop
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var args = new Object[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiverNode.executeGeneric(frame);
|
||||
args[1] = owner;
|
||||
args[2] = isInIterable;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(args);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -33,33 +33,29 @@ 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,
|
||||
boolean isInIterable) {
|
||||
ExpressionNode[] argumentNodes) {
|
||||
|
||||
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[3 + argumentNodes.length];
|
||||
var args = new Object[2 + 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[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(args);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -45,31 +45,27 @@ 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 isInIterable) {
|
||||
boolean needsConst) {
|
||||
|
||||
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,
|
||||
boolean isInIterable) {
|
||||
this(sourceSection, methodName, argumentNodes, lookupMode, false, isInIterable);
|
||||
MemberLookupMode lookupMode) {
|
||||
this(sourceSection, methodName, argumentNodes, lookupMode, false);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -89,12 +85,11 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
var args = new Object[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver.getThisValue();
|
||||
args[1] = receiver;
|
||||
args[2] = isInIterable;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(args);
|
||||
@@ -109,12 +104,11 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
@SuppressWarnings("unused") VmClass receiverClass,
|
||||
@Exclusive @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
var args = new Object[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver.getThisValue();
|
||||
args[1] = receiver;
|
||||
args[2] = isInIterable;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(receiver.getCallTarget(), args);
|
||||
@@ -130,12 +124,11 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
@Cached("resolveMethod(receiverClass)") ClassMethod method,
|
||||
@Cached("create(method.getCallTarget(sourceSection))") DirectCallNode callNode) {
|
||||
|
||||
var args = new Object[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver;
|
||||
args[1] = method.getOwner();
|
||||
args[2] = isInIterable;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(args);
|
||||
@@ -150,12 +143,11 @@ public abstract class InvokeMethodVirtualNode extends ExpressionNode {
|
||||
@Exclusive @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
var method = resolveMethod(receiverClass);
|
||||
var args = new Object[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = receiver;
|
||||
args[1] = method.getOwner();
|
||||
args[2] = isInIterable;
|
||||
for (var i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
// Deprecation should not report here (getCallTarget(sourceSection)), as this happens for each
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -30,18 +30,15 @@ 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();
|
||||
@@ -57,12 +54,11 @@ 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[3 + argumentNodes.length];
|
||||
var args = new Object[2 + argumentNodes.length];
|
||||
args[0] = VmUtils.getReceiverOrNull(frame);
|
||||
args[1] = supermethod.getOwner();
|
||||
args[2] = isInIterable;
|
||||
for (int i = 0; i < argumentNodes.length; i++) {
|
||||
args[3 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
args[2 + i] = argumentNodes[i].executeGeneric(frame);
|
||||
}
|
||||
|
||||
return callNode.call(args);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -50,7 +50,6 @@ 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,
|
||||
@@ -59,8 +58,7 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
boolean isBaseModule,
|
||||
boolean isCustomThisScope,
|
||||
ConstLevel constLevel,
|
||||
int constDepth,
|
||||
boolean isInIterable) {
|
||||
int constDepth) {
|
||||
|
||||
super(sourceSection);
|
||||
|
||||
@@ -70,7 +68,6 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
this.isCustomThisScope = isCustomThisScope;
|
||||
this.constLevel = constLevel;
|
||||
this.constDepth = constDepth;
|
||||
this.isInIterable = isInIterable;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -94,11 +91,7 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
assert localMethod.isLocal();
|
||||
checkConst(currOwner, localMethod, levelsUp);
|
||||
return new InvokeMethodLexicalNode(
|
||||
sourceSection,
|
||||
localMethod.getCallTarget(sourceSection),
|
||||
levelsUp,
|
||||
argumentNodes,
|
||||
isInIterable);
|
||||
sourceSection, localMethod.getCallTarget(sourceSection), levelsUp, argumentNodes);
|
||||
}
|
||||
var method = currOwner.getVmClass().getDeclaredMethod(methodName);
|
||||
if (method != null) {
|
||||
@@ -106,11 +99,7 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
checkConst(currOwner, method, levelsUp);
|
||||
if (method.getDeclaringClass().isClosed()) {
|
||||
return new InvokeMethodLexicalNode(
|
||||
sourceSection,
|
||||
method.getCallTarget(sourceSection),
|
||||
levelsUp,
|
||||
argumentNodes,
|
||||
isInIterable);
|
||||
sourceSection, method.getCallTarget(sourceSection), levelsUp, argumentNodes);
|
||||
}
|
||||
|
||||
//noinspection ConstantConditions
|
||||
@@ -119,7 +108,6 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
methodName,
|
||||
argumentNodes,
|
||||
MemberLookupMode.IMPLICIT_LEXICAL,
|
||||
isInIterable,
|
||||
levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp),
|
||||
GetClassNodeGen.create(null));
|
||||
}
|
||||
@@ -134,7 +122,7 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
(CallTarget) localMethod.getCallTarget().call(currOwner, currOwner);
|
||||
|
||||
return new InvokeMethodLexicalNode(
|
||||
sourceSection, methodCallTarget, levelsUp, argumentNodes, isInIterable);
|
||||
sourceSection, methodCallTarget, levelsUp, argumentNodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -150,7 +138,7 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
if (method != null) {
|
||||
assert !method.isLocal();
|
||||
return new InvokeMethodDirectNode(
|
||||
sourceSection, method, new ConstantValueNode(baseModule), argumentNodes, isInIterable);
|
||||
sourceSection, method, new ConstantValueNode(baseModule), argumentNodes);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -170,7 +158,6 @@ public final class ResolveMethodNode extends ExpressionNode {
|
||||
argumentNodes,
|
||||
MemberLookupMode.IMPLICIT_THIS,
|
||||
needsConst,
|
||||
isInIterable,
|
||||
VmUtils.createThisNode(VmUtils.unavailableSourceSection(), isCustomThisScope),
|
||||
GetClassNodeGen.create(null));
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -21,7 +21,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.NodeInfo;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
/** `this` inside `CustomThisScope` (type constraint, object member predicate). */
|
||||
@@ -38,7 +37,7 @@ public final class CustomThisNode extends ExpressionNode {
|
||||
if (customThisSlot == -1) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
|
||||
customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID);
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
return frame.getAuxiliarySlot(customThisSlot);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -24,8 +24,6 @@ import org.pkl.core.ast.MemberLookupMode;
|
||||
import org.pkl.core.ast.builder.ConstLevel;
|
||||
import org.pkl.core.ast.expression.member.ReadLocalPropertyNode;
|
||||
import org.pkl.core.ast.expression.member.ReadPropertyNodeGen;
|
||||
import org.pkl.core.ast.frame.ReadAuxiliarySlotNode;
|
||||
import org.pkl.core.ast.frame.ReadEnclosingAuxiliarySlotNode;
|
||||
import org.pkl.core.ast.frame.ReadEnclosingFrameSlotNodeGen;
|
||||
import org.pkl.core.ast.frame.ReadFrameSlotNodeGen;
|
||||
import org.pkl.core.ast.member.Member;
|
||||
@@ -34,14 +32,23 @@ import org.pkl.core.runtime.*;
|
||||
/**
|
||||
* Resolves a variable name, for example `foo` in `x = foo`.
|
||||
*
|
||||
* <p>A variable name can refer to any of the following: - a (potentially `local`) property in the
|
||||
* lexical scope - a method or lambda parameter in the lexical scope - a base module property - a
|
||||
* property accessible through `this`
|
||||
* <p>A variable name can refer to any of the following:
|
||||
*
|
||||
* <ul>
|
||||
* <li>a method/lambda parameter or for-generator/let-expression variable in the lexical scope
|
||||
* <li>a (potentially `local`) property in the lexical scope
|
||||
* <li>a `pkl.base` module property
|
||||
* <li>a property accessible through `this`
|
||||
* </ul>
|
||||
*
|
||||
* <p>This node's task is to make a one-time decision between these alternatives for the call site
|
||||
* it represents.
|
||||
*/
|
||||
// TODO: Move this to parse time (required for supporting local variables, more efficient)
|
||||
// TODO: Move this to parse time
|
||||
// * more capable because more information is available
|
||||
// and AST customization beyond replacing this node is possible
|
||||
// * useful for runtime AST transformations, for example to implement property-based testing
|
||||
// * more efficient
|
||||
//
|
||||
// TODO: In REPL, undo replace if environment changes to make the following work.
|
||||
// Perhaps instrumenting this node in REPL would be a good solution.
|
||||
@@ -82,49 +89,18 @@ public final class ResolveVariableNode extends ExpressionNode {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
var localPropertyName = variableName.toLocalProperty();
|
||||
|
||||
// search the frame for auxiliary slots carrying this variable (placed by
|
||||
// `WriteForVariablesNode`)
|
||||
var variableSlot = VmUtils.findAuxiliarySlot(frame, localPropertyName);
|
||||
if (variableSlot == -1) {
|
||||
variableSlot = VmUtils.findAuxiliarySlot(frame, variableName);
|
||||
}
|
||||
if (variableSlot != -1) {
|
||||
return new ReadAuxiliarySlotNode(getSourceSection(), variableSlot);
|
||||
}
|
||||
// search the frame for slots carrying this variable
|
||||
variableSlot = VmUtils.findSlot(frame, localPropertyName);
|
||||
if (variableSlot == -1) {
|
||||
variableSlot = VmUtils.findSlot(frame, variableName);
|
||||
}
|
||||
if (variableSlot != -1) {
|
||||
return ReadFrameSlotNodeGen.create(getSourceSection(), variableSlot);
|
||||
}
|
||||
|
||||
var currFrame = frame;
|
||||
var currOwner = VmUtils.getOwner(currFrame);
|
||||
var levelsUp = 0;
|
||||
|
||||
// Search lexical scope for a matching method/lambda parameter, `for` generator variable, or
|
||||
// object property.
|
||||
// Search lexical scope for a matching function parameter, for-generator variable, or object
|
||||
// property.
|
||||
do {
|
||||
var parameterSlot = VmUtils.findSlot(currFrame, variableName);
|
||||
if (parameterSlot == -1) {
|
||||
parameterSlot = VmUtils.findSlot(currFrame, localPropertyName);
|
||||
}
|
||||
if (parameterSlot != -1) {
|
||||
var slot = findFrameSlot(currFrame, variableName, localPropertyName);
|
||||
if (slot != -1) {
|
||||
return levelsUp == 0
|
||||
? ReadFrameSlotNodeGen.create(getSourceSection(), parameterSlot)
|
||||
: ReadEnclosingFrameSlotNodeGen.create(getSourceSection(), parameterSlot, levelsUp);
|
||||
}
|
||||
var auxiliarySlot = VmUtils.findAuxiliarySlot(currFrame, variableName);
|
||||
if (auxiliarySlot == -1) {
|
||||
auxiliarySlot = VmUtils.findAuxiliarySlot(currFrame, localPropertyName);
|
||||
}
|
||||
if (auxiliarySlot != -1) {
|
||||
return levelsUp == 0
|
||||
? new ReadAuxiliarySlotNode(getSourceSection(), auxiliarySlot)
|
||||
: new ReadEnclosingAuxiliarySlotNode(getSourceSection(), auxiliarySlot, levelsUp);
|
||||
? ReadFrameSlotNodeGen.create(getSourceSection(), slot)
|
||||
: ReadEnclosingFrameSlotNodeGen.create(getSourceSection(), slot, levelsUp);
|
||||
}
|
||||
|
||||
var localMember = currOwner.getMember(localPropertyName);
|
||||
@@ -136,8 +112,8 @@ public final class ResolveVariableNode extends ExpressionNode {
|
||||
var value = localMember.getConstantValue();
|
||||
if (value != null) {
|
||||
// This is the only code path that resolves local constant properties.
|
||||
// Since this code path doesn't use ObjectMember.getCachedValue(),
|
||||
// there is no point in calling localMember.setCachedValue() either.
|
||||
// Since this code path doesn't call VmObject.getCachedValue(),
|
||||
// there is no point in calling VmObject.setCachedValue() either.
|
||||
return new ConstantValueNode(sourceSection, value);
|
||||
}
|
||||
|
||||
@@ -236,4 +212,17 @@ public final class ResolveVariableNode extends ExpressionNode {
|
||||
throw exceptionBuilder().evalError("propertyMustBeConst", variableName.toString()).build();
|
||||
}
|
||||
}
|
||||
|
||||
private static int findFrameSlot(VirtualFrame frame, Object identifier1, Object identifier2) {
|
||||
var descriptor = frame.getFrameDescriptor();
|
||||
// Search backwards. The for-generator implementation exploits this
|
||||
// to shadow a slot by appending a slot with the same name.
|
||||
for (var i = descriptor.getNumberOfSlots() - 1; i >= 0; i--) {
|
||||
var slotName = descriptor.getSlotName(i);
|
||||
if (slotName == identifier1 || slotName == identifier2) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024 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.frame;
|
||||
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
|
||||
public class ReadAuxiliarySlotNode extends ExpressionNode {
|
||||
private final int slot;
|
||||
|
||||
public ReadAuxiliarySlotNode(SourceSection sourceSection, int slot) {
|
||||
super(sourceSection);
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
return frame.getAuxiliarySlot(slot);
|
||||
}
|
||||
}
|
||||
@@ -1,51 +0,0 @@
|
||||
/*
|
||||
* Copyright © 2024 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.frame;
|
||||
|
||||
import com.oracle.truffle.api.frame.MaterializedFrame;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.ExplodeLoop;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
public class ReadEnclosingAuxiliarySlotNode extends ExpressionNode {
|
||||
private final int slot;
|
||||
private final int levelsUp;
|
||||
|
||||
public ReadEnclosingAuxiliarySlotNode(SourceSection sourceSection, int slot, int levelsUp) {
|
||||
super(sourceSection);
|
||||
this.slot = slot;
|
||||
this.levelsUp = levelsUp;
|
||||
}
|
||||
|
||||
// could be factored out into a separate node s.t. it
|
||||
// won't be repeated in case of FrameSlotTypeException
|
||||
@ExplodeLoop
|
||||
protected final MaterializedFrame getCapturedFrame(VirtualFrame frame) {
|
||||
var owner = VmUtils.getOwner(frame);
|
||||
for (var i = 0; i < levelsUp - 1; i++) {
|
||||
owner = owner.getEnclosingOwner();
|
||||
assert owner != null; // guaranteed by AstBuilder
|
||||
}
|
||||
return owner.getEnclosingFrame();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
return getCapturedFrame(frame).getAuxiliarySlot(slot);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -80,7 +80,6 @@ public abstract class ToStringNode extends UnaryExpressionNode {
|
||||
Identifier.TO_STRING,
|
||||
new ExpressionNode[] {},
|
||||
MemberLookupMode.EXPLICIT_RECEIVER,
|
||||
false,
|
||||
null,
|
||||
null);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -33,12 +33,12 @@ public abstract class ApplyVmFunction0Node extends PklNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false);
|
||||
return callNode.call(function.getThisValue(), function);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalDirect")
|
||||
protected Object eval(VmFunction function, @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function, false);
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -77,13 +77,13 @@ public abstract class ApplyVmFunction1Node extends ExpressionNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, arg1);
|
||||
return callNode.call(function.getThisValue(), function, arg1);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalDirect")
|
||||
protected Object eval(
|
||||
VmFunction function, Object arg1, @Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function, false, arg1);
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function, arg1);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -76,7 +76,7 @@ public abstract class ApplyVmFunction2Node extends PklNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, arg1, arg2);
|
||||
return callNode.call(function.getThisValue(), function, arg1, arg2);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalDirect")
|
||||
@@ -86,7 +86,6 @@ public abstract class ApplyVmFunction2Node extends PklNode {
|
||||
Object arg2,
|
||||
@Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
return callNode.call(
|
||||
function.getCallTarget(), function.getThisValue(), function, false, arg1, arg2);
|
||||
return callNode.call(function.getCallTarget(), function.getThisValue(), function, arg1, arg2);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -36,7 +36,7 @@ public abstract class ApplyVmFunction3Node extends PklNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3);
|
||||
return callNode.call(function.getThisValue(), function, 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, false, arg1, arg2, arg3);
|
||||
function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -38,7 +38,7 @@ public abstract class ApplyVmFunction4Node extends PklNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3, arg4);
|
||||
return callNode.call(function.getThisValue(), function, 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, false, arg1, arg2, arg3, arg4);
|
||||
function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3, arg4);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -39,7 +39,7 @@ public abstract class ApplyVmFunction5Node extends PklNode {
|
||||
RootCallTarget cachedCallTarget,
|
||||
@Cached("create(cachedCallTarget)") DirectCallNode callNode) {
|
||||
|
||||
return callNode.call(function.getThisValue(), function, false, arg1, arg2, arg3, arg4, arg5);
|
||||
return callNode.call(function.getThisValue(), function, arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
|
||||
@Specialization(replaces = "evalDirect")
|
||||
@@ -53,14 +53,6 @@ public abstract class ApplyVmFunction5Node extends PklNode {
|
||||
@Cached("create()") IndirectCallNode callNode) {
|
||||
|
||||
return callNode.call(
|
||||
function.getCallTarget(),
|
||||
function.getThisValue(),
|
||||
function,
|
||||
false,
|
||||
arg1,
|
||||
arg2,
|
||||
arg3,
|
||||
arg4,
|
||||
arg5);
|
||||
function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3, arg4, arg5);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -43,11 +43,7 @@ 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.
|
||||
//
|
||||
// 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 static final int IMPLICIT_PARAM_COUNT = 2;
|
||||
|
||||
private final int paramCount;
|
||||
private final int totalParamCount;
|
||||
@@ -112,15 +108,9 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
throw wrongArgumentCount(totalArgCount - IMPLICIT_PARAM_COUNT);
|
||||
}
|
||||
|
||||
var isInIterable = (boolean) frame.getArguments()[2];
|
||||
|
||||
for (var i = 0; i < parameterTypeNodes.length; i++) {
|
||||
var argument = frame.getArguments()[IMPLICIT_PARAM_COUNT + i];
|
||||
if (isInIterable) {
|
||||
parameterTypeNodes[i].executeEagerlyAndSet(frame, argument);
|
||||
} else {
|
||||
parameterTypeNodes[i].executeAndSet(frame, argument);
|
||||
}
|
||||
parameterTypeNodes[i].executeAndSet(frame, argument);
|
||||
}
|
||||
|
||||
var result = bodyNode.executeGeneric(frame);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -130,20 +130,4 @@ public abstract class Member {
|
||||
public final boolean isLocalOrExternalOrAbstract() {
|
||||
return VmModifier.isLocalOrExternalOrAbstract(modifiers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tells if this member is declared inside the iterable of a for-generator, or an object spread.
|
||||
*
|
||||
* <p>This is {@code true} for {@code new {}} within:
|
||||
*
|
||||
* <pre>{@code
|
||||
* for (x in new Listing { new {} }) {
|
||||
* ^^^^^^
|
||||
* // etc
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
public boolean isInIterable() {
|
||||
return VmModifier.isInIterable(modifiers);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -59,24 +59,8 @@ public final class PropertyTypeNode extends PklRootNode {
|
||||
return qualifiedPropertyName;
|
||||
}
|
||||
|
||||
private boolean isInIterable(VirtualFrame frame) {
|
||||
var args = frame.getArguments();
|
||||
return args.length >= 4 && args[3] instanceof Boolean b && b;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Object executeImpl(VirtualFrame frame) {
|
||||
if (isInIterable(frame)) {
|
||||
// There is currently a bug around resolving variables within the iterable of a for
|
||||
// generator or spread syntax (https://github.com/apple/pkl/issues/741)
|
||||
//
|
||||
// Normally, mappings/listings are type-checked lazily. However, this results in said
|
||||
// bug getting widened, for any object members declared in the iterable.
|
||||
//
|
||||
// As a workaround for now, prevent the bug from being any worse by ensuring that these
|
||||
// object members are eagerly typechecked.
|
||||
return typeNode.executeEagerly(frame, frame.getArguments()[2]);
|
||||
}
|
||||
return typeNode.execute(frame, frame.getArguments()[2]);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -55,8 +55,7 @@ public abstract class TypeCheckedPropertyNode extends RegularMemberNode {
|
||||
|
||||
// TODO: propagate SUPER_CALL_MARKER to disable constraint (but not type) check
|
||||
if (callNode != null && VmUtils.shouldRunTypeCheck(frame)) {
|
||||
return callNode.call(
|
||||
VmUtils.getReceiverOrNull(frame), property.getOwner(), result, member.isInIterable());
|
||||
return callNode.call(VmUtils.getReceiverOrNull(frame), property.getOwner(), result);
|
||||
}
|
||||
|
||||
return result;
|
||||
@@ -76,8 +75,7 @@ public abstract class TypeCheckedPropertyNode extends RegularMemberNode {
|
||||
typeAnnNode.getCallTarget(),
|
||||
VmUtils.getReceiverOrNull(frame),
|
||||
property.getOwner(),
|
||||
result,
|
||||
member.isInIterable());
|
||||
result);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -47,10 +47,7 @@ public final class TypedPropertyNode extends RegularMemberNode {
|
||||
var propertyValue = bodyNode.executeGeneric(frame);
|
||||
if (VmUtils.shouldRunTypeCheck(frame)) {
|
||||
return typeCheckCallNode.call(
|
||||
VmUtils.getReceiver(frame),
|
||||
VmUtils.getOwner(frame),
|
||||
propertyValue,
|
||||
member.isInIterable());
|
||||
VmUtils.getReceiver(frame), VmUtils.getOwner(frame), propertyValue);
|
||||
}
|
||||
return propertyValue;
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -54,14 +54,14 @@ public final class IdentityMixinNode extends PklRootNode {
|
||||
@Override
|
||||
protected Object executeImpl(VirtualFrame frame) {
|
||||
var arguments = frame.getArguments();
|
||||
if (arguments.length != 4) {
|
||||
if (arguments.length != 3) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw exceptionBuilder()
|
||||
.evalError("wrongFunctionArgumentCount", 1, arguments.length - 3)
|
||||
.evalError("wrongFunctionArgumentCount", 1, arguments.length - 2)
|
||||
.withSourceSection(sourceSection)
|
||||
.build();
|
||||
}
|
||||
var argument = arguments[3];
|
||||
var argument = arguments[2];
|
||||
if (argumentTypeNode != null) {
|
||||
return argumentTypeNode.execute(frame, argument);
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -25,7 +25,6 @@ import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.PklNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.ast.lambda.ApplyVmFunction1Node;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.VmFunction;
|
||||
@@ -87,7 +86,7 @@ public abstract class TypeConstraintNode extends PklNode {
|
||||
if (customThisSlot == -1) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
// deferred until execution time s.t. nodes of inlined type aliases get the right frame slot
|
||||
customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID);
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -95,14 +95,6 @@ public abstract class TypeNode extends PklNode {
|
||||
return execute(frame, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if {@code value} conforms to this type.
|
||||
*
|
||||
* <p>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,
|
||||
@@ -226,11 +218,6 @@ 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 {
|
||||
@@ -248,13 +235,6 @@ 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;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -288,13 +268,6 @@ 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. */
|
||||
@@ -360,11 +333,6 @@ 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;
|
||||
@@ -2435,22 +2403,6 @@ 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);
|
||||
setOwner(frame, VmUtils.getOwner(typeAlias.getEnclosingFrame()));
|
||||
setReceiver(frame, VmUtils.getReceiver(typeAlias.getEnclosingFrame()));
|
||||
|
||||
try {
|
||||
return aliasedTypeNode.executeEagerlyAndSet(frame, value);
|
||||
} finally {
|
||||
setOwner(frame, prevOwner);
|
||||
setReceiver(frame, prevReceiver);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@TruffleBoundary
|
||||
public @Nullable Object createDefaultValue(
|
||||
@@ -2586,13 +2538,6 @@ 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) {
|
||||
@@ -2740,11 +2685,6 @@ 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();
|
||||
@@ -2813,11 +2753,6 @@ 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();
|
||||
@@ -2858,11 +2793,6 @@ 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();
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -141,9 +141,6 @@ public final class Identifier implements Comparable<Identifier> {
|
||||
// common in lambdas etc
|
||||
public static final Identifier IT = get("it");
|
||||
|
||||
// dummy, unrepresentable identifier
|
||||
public static final Identifier DUMMY = get("`#_");
|
||||
|
||||
private final String name;
|
||||
|
||||
private Identifier(String name) {
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -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, false, arg1);
|
||||
return getCallTarget().call(thisValue, this, 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, false, arg1, arg2);
|
||||
return getCallTarget().call(thisValue, this, arg1, arg2);
|
||||
}
|
||||
|
||||
public VmFunction copy(
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -23,8 +23,8 @@ import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.util.Nullable;
|
||||
|
||||
/**
|
||||
* Corresponds to `pkl.base#Object|pkl.base#Function`. The lexical scope is a hierarchy of
|
||||
* `VmOwner`s.
|
||||
* Corresponds to `pkl.base#Object|pkl.base#Function`. The lexical scope is a chain of
|
||||
* `VmObjectLike` instances.
|
||||
*/
|
||||
public abstract class VmObjectLike extends VmValue {
|
||||
/** The frame that was active when this object was instantiated. * */
|
||||
@@ -66,8 +66,8 @@ public abstract class VmObjectLike extends VmValue {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the parent object in the prototype chain. For each concrete subclass X of VmOwner, the
|
||||
* exact return type of this method is `X|VmTyped`.
|
||||
* Returns the parent object in the prototype chain. For each concrete subclass X of VmObjectLike,
|
||||
* the exact return type of this method is `X|VmTyped`.
|
||||
*/
|
||||
public abstract @Nullable VmObjectLike getParent();
|
||||
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -47,6 +47,7 @@ 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.*;
|
||||
@@ -300,6 +301,59 @@ public final class VmUtils {
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies {@code numberOfLocalsToCopy} locals from {@code sourceFrame}, starting at {@code
|
||||
* firstSourceSlot}, to {@code firstSourceSlot}, starting at {@code firstTargetSlot}.
|
||||
*/
|
||||
public static void copyLocals(
|
||||
VirtualFrame sourceFrame,
|
||||
int firstSourceSlot,
|
||||
VirtualFrame targetFrame,
|
||||
int firstTargetSlot,
|
||||
int numberOfLocalsToCopy) {
|
||||
var sourceDescriptor = sourceFrame.getFrameDescriptor();
|
||||
var targetDescriptor = targetFrame.getFrameDescriptor();
|
||||
// Alternatively, locals could be copied with `numberOfLocalsToCopy`
|
||||
// `ReadFrameSlotNode/WriteFrameSlotNode`'s.
|
||||
for (int i = 0; i < numberOfLocalsToCopy; i++) {
|
||||
var sourceSlot = firstSourceSlot + i;
|
||||
var targetSlot = firstTargetSlot + i;
|
||||
// If, for a particular call site of this method,
|
||||
// slot kinds of `sourceDescriptor` will reach a steady state,
|
||||
// then slot kinds of `targetDescriptor` will too.
|
||||
var slotKind = sourceDescriptor.getSlotKind(sourceSlot);
|
||||
switch (slotKind) {
|
||||
case Boolean -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Boolean);
|
||||
targetFrame.setBoolean(targetSlot, sourceFrame.getBoolean(sourceSlot));
|
||||
}
|
||||
case Long -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Long);
|
||||
targetFrame.setLong(targetSlot, sourceFrame.getLong(sourceSlot));
|
||||
}
|
||||
case Double -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Double);
|
||||
targetFrame.setDouble(targetSlot, sourceFrame.getDouble(sourceSlot));
|
||||
}
|
||||
case Object -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Object);
|
||||
targetFrame.setObject(
|
||||
targetSlot,
|
||||
sourceFrame instanceof MaterializedFrame
|
||||
// Even though sourceDescriptor.getSlotKind is now Object,
|
||||
// it may have been a primitive kind when `sourceFrame`'s local was written.
|
||||
// Hence we need to read the local with getValue() instead of getObject().
|
||||
? sourceFrame.getValue(sourceSlot)
|
||||
: sourceFrame.getObject(sourceSlot));
|
||||
}
|
||||
default -> {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
throw new VmExceptionBuilder().bug("Unexpected FrameSlotKind: " + slotKind).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A failed property type check looks as follows in the stack trace:
|
||||
// x: Int(isPositive)
|
||||
// at ...
|
||||
@@ -687,9 +741,7 @@ public final class VmUtils {
|
||||
}
|
||||
|
||||
public static TypeNode[] resolveParameterTypes(
|
||||
VirtualFrame frame,
|
||||
FrameDescriptor descriptor,
|
||||
@Nullable UnresolvedTypeNode[] parameterTypeNodes) {
|
||||
VirtualFrame frame, FrameDescriptor descriptor, UnresolvedTypeNode[] parameterTypeNodes) {
|
||||
|
||||
var resolvedNodes = new TypeNode[parameterTypeNodes.length];
|
||||
|
||||
@@ -836,25 +888,11 @@ public final class VmUtils {
|
||||
return callNode.call(rootNode.getCallTarget(), module, module);
|
||||
}
|
||||
|
||||
public static int findSlot(VirtualFrame frame, Object identifier) {
|
||||
var descriptor = frame.getFrameDescriptor();
|
||||
for (int i = 0; i < descriptor.getNumberOfSlots(); i++) {
|
||||
if (descriptor.getSlotName(i) == identifier
|
||||
&& frame.getTag(i) != FrameSlotKind.Illegal.tag
|
||||
// Truffle initializes all frame tags as `FrameSlotKind.Object`, instead of
|
||||
// `FrameSlotKind.Illegal`. Unevaluated (in a `when` with `false` condition) `for`
|
||||
// generators, therefore, leave their bound variables tagged as `Object`, but `null`. If
|
||||
// another `for` generator in the same scope binds variables with the same names, they
|
||||
// resolve the wrong slot number.
|
||||
&& (frame.getTag(i) != FrameSlotKind.Object.tag || frame.getObject(i) != null)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
public static int findAuxiliarySlot(VirtualFrame frame, Object identifier) {
|
||||
return frame.getFrameDescriptor().getAuxiliarySlots().getOrDefault(identifier, -1);
|
||||
public static int findCustomThisSlot(VirtualFrame frame) {
|
||||
return frame
|
||||
.getFrameDescriptor()
|
||||
.getAuxiliarySlots()
|
||||
.getOrDefault(CustomThisScope.FRAME_SLOT_ID, -1);
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
/*
|
||||
* Copyright © 2024 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
* Copyright © 2024-2025 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.
|
||||
@@ -31,12 +31,11 @@ public final class FunctionNodes {
|
||||
protected Object eval(VmFunction self, VmList argList) {
|
||||
var argCount = argList.getLength();
|
||||
|
||||
var args = new Object[3 + argCount];
|
||||
var args = new Object[2 + argCount];
|
||||
args[0] = self.getThisValue();
|
||||
args[1] = self;
|
||||
args[2] = false;
|
||||
|
||||
var i = 3;
|
||||
var i = 2;
|
||||
for (var arg : argList) {
|
||||
args[i++] = arg;
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
a = List(1, 2, 3, 4)
|
||||
|
||||
b = List("a", "b", "c", "d")
|
||||
|
||||
foo {
|
||||
for (_dup, i in a) {
|
||||
for (_dup, j in b) {
|
||||
i + j
|
||||
}
|
||||
}
|
||||
}
|
||||
18
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl
vendored
Normal file
18
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl
vendored
Normal file
@@ -0,0 +1,18 @@
|
||||
function myMethod(arg) = new {
|
||||
arg = "property" // same name as method arg
|
||||
for (key, value in List("one", "two", arg)) { // `arg` resolves to method arg
|
||||
[Pair(arg, key)] = // `arg` resolves to method arg
|
||||
Pair(arg, value) // `arg` resolves to object property
|
||||
}
|
||||
}
|
||||
|
||||
local myLambda = (arg) -> new Dynamic {
|
||||
arg = "property" // same name as lambda arg
|
||||
for (key, value in List("one", "two", arg)) { // `arg` resolves to lambda arg
|
||||
[Pair(arg, key)] = // `arg` resolves to lambda arg
|
||||
Pair(arg, value) // `arg` resolves to object property
|
||||
}
|
||||
}
|
||||
|
||||
res1 = myMethod("three")
|
||||
res2 = myLambda.apply("three")
|
||||
25
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl
vendored
Normal file
25
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl
vendored
Normal file
@@ -0,0 +1,25 @@
|
||||
// https://github.com/apple/pkl/issues/741
|
||||
|
||||
bar = new {}
|
||||
|
||||
res1 {
|
||||
for (i in List(1)) {
|
||||
...(bar) {
|
||||
baz {
|
||||
new { i }
|
||||
}
|
||||
}.baz
|
||||
}
|
||||
}
|
||||
|
||||
res2 {
|
||||
for (i in List(1)) {
|
||||
for (elem in (bar) {
|
||||
baz {
|
||||
new { i }
|
||||
}
|
||||
}.baz) {
|
||||
elem
|
||||
}
|
||||
}
|
||||
}
|
||||
33
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl
vendored
Normal file
33
pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl
vendored
Normal file
@@ -0,0 +1,33 @@
|
||||
examples {
|
||||
local a = List("1", "2", "3", "4")
|
||||
local b = List("a", "b", "c", "d")
|
||||
|
||||
["shadow key variable"] {
|
||||
new {
|
||||
for (key, outerValue in a) {
|
||||
for (key, innerValue in b) {
|
||||
List(outerValue, key, innerValue)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
["shadow value variable"] {
|
||||
new {
|
||||
for (outerKey, value in a) {
|
||||
for (innerKey, value in b) {
|
||||
List(outerKey, value, innerKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
["sibling for-generators can use same variable names"] {
|
||||
new {
|
||||
for (key, value in a) {
|
||||
List(key, value)
|
||||
}
|
||||
for (key, value in b) {
|
||||
List(key, value)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
–– Pkl Error ––
|
||||
Duplicate definition of member `_dup`.
|
||||
|
||||
x | for (_dup, j in b) {
|
||||
^^^^
|
||||
at forGeneratorDuplicateParams2#foo (file:///$snippetsDir/input/errors/forGeneratorDuplicateParams2.pkl)
|
||||
12
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf
vendored
Normal file
12
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf
vendored
Normal file
@@ -0,0 +1,12 @@
|
||||
res1 {
|
||||
arg = "property"
|
||||
[Pair("three", 0)] = Pair("property", "one")
|
||||
[Pair("three", 1)] = Pair("property", "two")
|
||||
[Pair("three", 2)] = Pair("property", "three")
|
||||
}
|
||||
res2 {
|
||||
arg = "property"
|
||||
[Pair("three", 0)] = Pair("property", "one")
|
||||
[Pair("three", 1)] = Pair("property", "two")
|
||||
[Pair("three", 2)] = Pair("property", "three")
|
||||
}
|
||||
11
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf
vendored
Normal file
11
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
bar {}
|
||||
res1 {
|
||||
new {
|
||||
1
|
||||
}
|
||||
}
|
||||
res2 {
|
||||
new {
|
||||
1
|
||||
}
|
||||
}
|
||||
54
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf
vendored
Normal file
54
pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
examples {
|
||||
["shadow key variable"] {
|
||||
new {
|
||||
List("1", 0, "a")
|
||||
List("1", 1, "b")
|
||||
List("1", 2, "c")
|
||||
List("1", 3, "d")
|
||||
List("2", 0, "a")
|
||||
List("2", 1, "b")
|
||||
List("2", 2, "c")
|
||||
List("2", 3, "d")
|
||||
List("3", 0, "a")
|
||||
List("3", 1, "b")
|
||||
List("3", 2, "c")
|
||||
List("3", 3, "d")
|
||||
List("4", 0, "a")
|
||||
List("4", 1, "b")
|
||||
List("4", 2, "c")
|
||||
List("4", 3, "d")
|
||||
}
|
||||
}
|
||||
["shadow value variable"] {
|
||||
new {
|
||||
List(0, "a", 0)
|
||||
List(0, "b", 1)
|
||||
List(0, "c", 2)
|
||||
List(0, "d", 3)
|
||||
List(1, "a", 0)
|
||||
List(1, "b", 1)
|
||||
List(1, "c", 2)
|
||||
List(1, "d", 3)
|
||||
List(2, "a", 0)
|
||||
List(2, "b", 1)
|
||||
List(2, "c", 2)
|
||||
List(2, "d", 3)
|
||||
List(3, "a", 0)
|
||||
List(3, "b", 1)
|
||||
List(3, "c", 2)
|
||||
List(3, "d", 3)
|
||||
}
|
||||
}
|
||||
["sibling for-generators can use same variable names"] {
|
||||
new {
|
||||
List(0, "1")
|
||||
List(1, "2")
|
||||
List(2, "3")
|
||||
List(3, "4")
|
||||
List(0, "a")
|
||||
List(1, "b")
|
||||
List(2, "c")
|
||||
List(3, "d")
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user