diff --git a/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java index 6d9d738db..e2dad48ba 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/MemberNode.java @@ -16,9 +16,7 @@ package org.pkl.core.ast; import com.oracle.truffle.api.frame.FrameDescriptor; -import com.oracle.truffle.api.frame.VirtualFrame; 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; @@ -44,14 +42,6 @@ public final ExpressionNode getBodyNode() { return bodyNode; } - public final void replaceBody(Function replacer) { - bodyNode = insert(replacer.apply(bodyNode)); - } - - protected final Object executeBody(VirtualFrame frame) { - return executeBody(frame, bodyNode); - } - protected final VmExceptionBuilder exceptionBuilder() { return new VmExceptionBuilder().withSourceSection(getHeaderSection()); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/PklRootNode.java b/pkl-core/src/main/java/org/pkl/core/ast/PklRootNode.java index 089f6c779..ef8c85e51 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/PklRootNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/PklRootNode.java @@ -22,6 +22,7 @@ import com.oracle.truffle.api.nodes.NodeInfo; import com.oracle.truffle.api.nodes.RootNode; import com.oracle.truffle.api.source.SourceSection; +import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.runtime.*; import org.pkl.core.util.Nullable; @@ -36,9 +37,16 @@ protected PklRootNode(@Nullable VmLanguage language, FrameDescriptor descriptor) public abstract @Nullable String getName(); - protected final Object executeBody(VirtualFrame frame, ExpressionNode bodyNode) { + // name must start with `execute` (see Javadoc of @Specialization) + protected abstract Object executeImpl(VirtualFrame frame); + + @Override + public final Object execute(VirtualFrame frame) { try { - return bodyNode.executeGeneric(frame); + return executeImpl(frame); + } catch (VmTypeMismatchException e) { + CompilerDirectives.transferToInterpreter(); + throw e.toVmException(); } catch (VmException e) { CompilerDirectives.transferToInterpreter(); throw e; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/SimpleRootNode.java b/pkl-core/src/main/java/org/pkl/core/ast/SimpleRootNode.java index d5a2388de..77b21c2c8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/SimpleRootNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/SimpleRootNode.java @@ -49,7 +49,7 @@ public String getName() { } @Override - public Object execute(VirtualFrame frame) { - return executeBody(frame, bodyNode); + protected Object executeImpl(VirtualFrame frame) { + return bodyNode.executeGeneric(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java b/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java index 097ce0666..d13995828 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/VmModifier.java @@ -55,9 +55,6 @@ private 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 static boolean isEntry(int modifiers) { 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; } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java index 7f65c54b3..6ad6da549 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/AstBuilder.java @@ -25,7 +25,6 @@ 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.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 @@ private GeneratorObjectLiteralNode doVisitGeneratorObjectBody( @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 @@ private void checkHasNoForGenerator(ParserRuleContext ctx, String errorMessageKe @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 GeneratorMemberNode visitObjectEntry(ObjectEntryContext ctx) { 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 GeneratorWhenNode visitWhenGenerator(WhenGeneratorContext ctx) { 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 @@ private static boolean isIgnored(@Nullable ParameterContext param) { @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 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 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 scope = symbolTable.getCurrentScope(); - var visitingIterable = scope.isVisitingIterable(); - scope.setVisitingIterable(true); + var currentScope = symbolTable.getCurrentScope(); + 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); + } + 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 @@ private void addConstantEntries( } 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 @@ private ObjectMember doVisitObjectElement(ObjectElementContext ctx) { 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 @@ private ObjectMember doVisitObjectElement(ObjectElementContext ctx) { }); } - private Pair 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 doVisitObjectEntry(ObjectEntryContext ctx) { checkClosingDelimiter(ctx.err1, "]", ctx.k.stop); if (ctx.err2 != null) { @@ -1253,46 +1227,54 @@ private Pair doVisitObjectEntry(ObjectEntryContext } 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> objectMemberInserter( + private ObjectMember doVisitObjectEntryBody( SourceSection sourceSection, ExpressionNode keyNode, @Nullable ExprContext valueCtx, List 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()); - - 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)); - } + 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)); + } - return Pair.of(keyNode, member); - }; + return member; + }); } @Override @@ -1350,10 +1332,6 @@ private int doVisitModifiers( 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 @@ private ExpressionNode doVisitMethodAccessExpr(QualifiedAccessExprContext ctx) { visitArgumentList(argCtx), MemberLookupMode.EXPLICIT_RECEIVER, needsConst, - symbolTable.getCurrentScope().isVisitingIterable(), PropagateNullReceiverNodeGen.create(unavailableSourceSection(), receiver), GetClassNodeGen.create(null))); } @@ -1999,7 +1976,6 @@ private ExpressionNode doVisitMethodAccessExpr(QualifiedAccessExprContext ctx) { visitArgumentList(argCtx), MemberLookupMode.EXPLICIT_RECEIVER, needsConst, - symbolTable.getCurrentScope().isVisitingIterable(), receiver, GetClassNodeGen.create(null)); } @@ -2074,11 +2050,7 @@ public ExpressionNode visitSuperAccessExpr(SuperAccessExprContext ctx) { } return InvokeSuperMethodNodeGen.create( - sourceSection, - memberName, - symbolTable.getCurrentScope().isVisitingIterable(), - visitArgumentList(argCtx), - needsConst); + sourceSection, memberName, visitArgumentList(argCtx), needsConst); } // superproperty call @@ -2136,8 +2108,7 @@ public ExpressionNode visitUnqualifiedAccessExpr(UnqualifiedAccessExprContext ct isBaseModule, scope.isCustomThisScope(), scope.getConstLevel(), - scope.getConstDepth(), - scope.isVisitingIterable()); + scope.getConstDepth()); } @Override diff --git a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java index 6fec38568..e85706ab0 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/builder/SymbolTable.java @@ -17,15 +17,14 @@ 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 @@ 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 T enterMethod( nodeFactory); } + public T enterForGenerator( + FrameDescriptor.Builder frameDescriptorBuilder, + FrameDescriptor.Builder memberDescriptorBuilder, + Function nodeFactory) { + return doEnter( + new ForGeneratorScope( + currentScope, + currentScope.qualifiedName, + frameDescriptorBuilder, + memberDescriptorBuilder), + nodeFactory); + } + public T enterLambda( FrameDescriptor.Builder frameDescriptorBuilder, Function nodeFactory) { @@ -128,9 +138,11 @@ public T enterEntry( Function 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 enterCustomThisScope(Function nodeFactory) { @@ -166,12 +178,10 @@ public abstract static class Scope { private final @Nullable Scope parent; private final @Nullable Identifier name; private final String qualifiedName; - private final Deque 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 @@ private Scope( 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 FrameDescriptor buildFrameDescriptor() { 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 (int 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 int getConstDepth() { return depth; } - /** - * Adds the for generator variable to the frame descriptor. - * - *

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 getForGeneratorVariables() { - return forGeneratorVariables; - } - private String getNextLambdaName() { return ""; } - 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,16 +345,12 @@ public final boolean isLexicalScope() { return this instanceof LexicalScope; } - public ConstLevel getConstLevel() { - return constLevel; - } - - public void setVisitingIterable(boolean isVisitingIterable) { - this.isVisitingIterable = isVisitingIterable; + public final boolean isForGeneratorScope() { + return this instanceof ForGeneratorScope; } - public boolean isVisitingIterable() { - return isVisitingIterable; + public ConstLevel getConstLevel() { + return constLevel; } } @@ -413,6 +418,30 @@ public LambdaScope( } } + 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, diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java index 6a3a63f61..9138ea94b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/binary/LetExprNode.java @@ -21,7 +21,6 @@ import com.oracle.truffle.api.nodes.DirectCallNode; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.ExpressionNode; -import org.pkl.core.ast.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 Object executeGeneric(VirtualFrame frame) { 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 Object executeGeneric(VirtualFrame frame) { var value = valueNode.executeGeneric(frame); - return callNode.call(function.getThisValue(), function, false, value); + return callNode.call(function.getThisValue(), function, value); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorElementNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorElementNode.java index 82c41ab4c..210cfc00d 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorElementNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorElementNode.java @@ -19,57 +19,50 @@ 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); - } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorEntryNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorEntryNode.java index 4aba77d18..6ccbc8deb 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorEntryNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorEntryNode.java @@ -25,15 +25,14 @@ 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 @@ void fallback(Object parent, ObjectData data) { 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 @@ private void addListingEntry(VirtualFrame frame, ObjectData data, int parentLeng .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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorForNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorForNode.java index edc3f2e7f..865b6c632 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorForNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorForNode.java @@ -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, + // null if for-generator doesn't bind key or `keyTypeNode` is passed instead of this node ExpressionNode iterableNode, @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 @@ protected abstract void executeWithIterable( @Override public final void execute(VirtualFrame frame, Object parent, ObjectData data) { + initialize(frame); executeWithIterable(frame, parent, data, iterableNode.executeGeneric(frame)); } @@ -99,41 +89,33 @@ protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmDynami @Specialization protected void eval(VirtualFrame frame, Object parent, ObjectData data, VmList iterable) { - initTypeNodes(frame); long idx = 0; - for (Object element : iterable) { + for (var 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, 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 @@ protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Obje .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); + iterable.forceAndIterateMemberValues( + (key, member, 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; + }); } - private void resetFrameSlots(VirtualFrame frame) { - if (keySlot != -1) { - frame.clear(keySlot); + @ExplodeLoop + private void executeIteration( + VirtualFrame frame, Object parent, ObjectData data, Object key, Object value) { + + // 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); + } + if (valueTypeNode != null) { + valueTypeNode.executeAndSet(newFrame, value); } - if (valueSlot != -1) { - frame.clear(valueSlot); + //noinspection ForLoopReplaceableByForEach + for (int i = 0; i < childNodes.length; i++) { + childNodes[i].execute(newFrame, parent, data); } } - private void initTypeNodes(VirtualFrame frame) { + private void initialize(VirtualFrame frame) { if (unresolvedKeyTypeNode != null) { - CompilerDirectives.transferToInterpreter(); + 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.transferToInterpreter(); + 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; } } - - /** - * Evaluate members upfront to make sure that `childNode.execute()` is not behind a Truffle - * boundary. - */ - @TruffleBoundary - private List> evaluateMembers(VmObject object) { - var members = new ArrayList>(); - object.forceAndIterateMemberValues( - (key, member, value) -> { - members.add(Pair.of(member.isProp() ? key.toString() : key, 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(); - } - - 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); - } - - for (var childNode : childNodes) { - childNode.execute(frame, parent, data); - } - - data.resetForBindings(prevBindings); - } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorMemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorMemberNode.java index a8e495ed9..df7fb9499 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorMemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorMemberNode.java @@ -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 @@ protected static boolean isTypedObjectClass(VmClass clazz) { } @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(); - } - - /** - * - * x = new Mapping { for (i in IntSeq(1, 3)) for (key, value in Map(4, "Pigeon", 6, "Barn Owl")) [i * - * key] = value.reverse() } - * - * - *

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 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 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; - } - return result; - } - - @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; + 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; - } - - void persistForBindings(Object key) { - EconomicMaps.put(forBindings, key, currentForBindings); + throw exception; } - - 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; } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorObjectLiteralNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorObjectLiteralNode.java index 33c43a103..54b176e61 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorObjectLiteralNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorObjectLiteralNode.java @@ -27,7 +27,6 @@ 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 @@ protected GeneratorObjectLiteralNode copy(ExpressionNode newParentNode) { @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 @@ protected Object evalNull(VirtualFrame frame, VmNull parent) { } @Specialization(guards = "checkIsValidFunctionAmendment(parent)") - protected Object evalFunction( + protected VmFunction evalFunction( VirtualFrame frame, VmFunction parent, @Cached(value = "createAmendFunctionNode(frame)", neverDefault = true) @@ -120,41 +116,34 @@ protected Object evalFunction( } @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 @@ protected boolean checkMappingCannotHaveParameters() { } @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; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java index 95f1013ad..370569a69 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPredicateMemberNode.java @@ -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 @@ private void addMembers(VirtualFrame frame, VmObject parent, ObjectData data) { 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 @@ private void initThisSlot(VirtualFrame frame) { 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); - } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPropertyNode.java index bb24b82b6..06549348c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorPropertyNode.java @@ -20,67 +20,67 @@ 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 @@ protected boolean checkIsValidMappingProperty() { .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); - } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorSpreadNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorSpreadNode.java index fb8945b40..298495acb 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorSpreadNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorSpreadNode.java @@ -19,7 +19,6 @@ 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.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.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 @@ protected void eval(VmObject parent, ObjectData data, VmNull iterable) { @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 @@ protected void fallback(VirtualFrame frame, Object parent, ObjectData data, Obje 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 @@ private void doEvalCollection(VmClass parent, ObjectData data, VmCollection iter .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 @@ private void doEvalMap(VmClass parent, ObjectData data, VmMap iterable) { } 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 @@ private void doEvalIntSeq(VmClass parent, ObjectData data, VmIntSeq iterable) { .withProgramValue("Value", iterable) .build(); } - spreadIterable(data, iterable); + spreadIterable(frame, data, iterable); } private void cannotHaveMember(VmClass clazz, ObjectMember member) { @@ -295,7 +288,8 @@ private void cannotHaveMember(VmClass clazz, ObjectMember member) { 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 @@ private ObjectMember createMember(ObjectMember prototype, Object value) { 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); } } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorWhenNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorWhenNode.java index e35e1beb5..919f1f846 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorWhenNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/GeneratorWhenNode.java @@ -34,7 +34,7 @@ public GeneratorWhenNode( GeneratorMemberNode[] thenNodes, GeneratorMemberNode[] elseNodes) { - super(sourceSection); + super(sourceSection, false); this.conditionNode = conditionNode; this.thenNodes = thenNodes; this.elseNodes = elseNodes; diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/ObjectData.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/ObjectData.java new file mode 100644 index 000000000..03ae904c2 --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/ObjectData.java @@ -0,0 +1,90 @@ +/* + * 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.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 members; + // The frames that were active when `members` were generated. + // Only a subset of members have their frames stored (`GeneratorMemberNode.isFrameStored`). + // Frames are stored in `owner.extraStorage` and retrieved by `RestoreForBindingsNode` + // when members are executed. + private final EconomicMap 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 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 storeGeneratorFrames(T object) { + object.setExtraStorage(generatorFrames); + return object; + } + + static MaterializedFrame getGeneratorFrame(VirtualFrame frame) { + @SuppressWarnings("unchecked") + var map = (EconomicMap) VmUtils.getOwner(frame).getExtraStorage(); + var result = EconomicMaps.get(map, VmUtils.getMemberKey(frame)); + assert result != null; + return result; + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/RestoreForBindingsNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/RestoreForBindingsNode.java new file mode 100644 index 000000000..cac2bb32d --- /dev/null +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/RestoreForBindingsNode.java @@ -0,0 +1,46 @@ +/* + * 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 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); + } +} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/WriteForVariablesNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/WriteForVariablesNode.java deleted file mode 100644 index 23659a424..000000000 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/generator/WriteForVariablesNode.java +++ /dev/null @@ -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) 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); - } -} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java index 337c777b2..7665ffc12 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/AmendFunctionNode.java @@ -26,8 +26,6 @@ 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 AmendFunctionNode( } 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 AmendFunctionNode( 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 Object executeGeneric(VirtualFrame frame) { 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)) { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/FunctionLiteralNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/FunctionLiteralNode.java index 2941fbc25..5169e82c8 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/FunctionLiteralNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/FunctionLiteralNode.java @@ -20,7 +20,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.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 VmFunction executeGeneric(VirtualFrame frame) { CompilerDirectives.transferToInterpreterAndInvalidate(); functionNode = unresolvedFunctionNode.execute(frame); if (isCustomThisScope) { - customThisSlot = VmUtils.findAuxiliarySlot(frame, CustomThisScope.FRAME_SLOT_ID); + customThisSlot = VmUtils.findCustomThisSlot(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/ObjectLiteralNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/ObjectLiteralNode.java index 6822650d5..c1588030d 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/ObjectLiteralNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/literal/ObjectLiteralNode.java @@ -66,7 +66,7 @@ protected final AmendFunctionNode createAmendFunctionNode(VirtualFrame frame) { parametersDescriptor == null ? new TypeNode[0] : VmUtils.resolveParameterTypes(frame, parametersDescriptor, parameterTypes); - return new AmendFunctionNode(this, resolvedParameterTypes, frame.getFrameDescriptor()); + return new AmendFunctionNode(this, resolvedParameterTypes); } @Idempotent diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java index 33fed39ba..517c98e4a 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodDirectNode.java @@ -28,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 InvokeMethodDirectNode( 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 InvokeMethodDirectNode( @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); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java index 7babf917e..37c5fc29f 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodLexicalNode.java @@ -33,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); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java index dd263a045..da38a5869 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeMethodVirtualNode.java @@ -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 @@ protected Object evalFunctionCached( 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 @@ protected Object evalFunction( @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 @@ protected Object evalCached( @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 @@ protected Object eval( @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 diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java index 700f13d13..e70e2fb87 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/InvokeSuperMethodNode.java @@ -30,18 +30,15 @@ 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 @@ protected Object eval( @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); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java index 737354819..d2d0b5516 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/member/ResolveMethodNode.java @@ -50,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 ResolveMethodNode( boolean isBaseModule, boolean isCustomThisScope, ConstLevel constLevel, - int constDepth, - boolean isInIterable) { + int constDepth) { super(sourceSection); @@ -70,7 +68,6 @@ public ResolveMethodNode( this.isCustomThisScope = isCustomThisScope; this.constLevel = constLevel; this.constDepth = constDepth; - this.isInIterable = isInIterable; } @Override @@ -94,11 +91,7 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { 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 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { 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 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { methodName, argumentNodes, MemberLookupMode.IMPLICIT_LEXICAL, - isInIterable, levelsUp == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(levelsUp), GetClassNodeGen.create(null)); } @@ -134,7 +122,7 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { (CallTarget) localMethod.getCallTarget().call(currOwner, currOwner); return new InvokeMethodLexicalNode( - sourceSection, methodCallTarget, levelsUp, argumentNodes, isInIterable); + sourceSection, methodCallTarget, levelsUp, argumentNodes); } } @@ -150,7 +138,7 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { 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 @@ private ExpressionNode doResolve(VmObjectLike initialOwner) { argumentNodes, MemberLookupMode.IMPLICIT_THIS, needsConst, - isInIterable, VmUtils.createThisNode(VmUtils.unavailableSourceSection(), isCustomThisScope), GetClassNodeGen.create(null)); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/CustomThisNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/CustomThisNode.java index 395841929..7f3b47097 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/CustomThisNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/CustomThisNode.java @@ -21,7 +21,6 @@ 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 Object executeGeneric(VirtualFrame frame) { 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); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ResolveVariableNode.java b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ResolveVariableNode.java index 932dfee04..a5e93a068 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ResolveVariableNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/expression/primary/ResolveVariableNode.java @@ -24,8 +24,6 @@ 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 @@ /** * Resolves a variable name, for example `foo` in `x = foo`. * - *

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` + *

A variable name can refer to any of the following: + * + *

    + *
  • a method/lambda parameter or for-generator/let-expression variable in the lexical scope + *
  • a (potentially `local`) property in the lexical scope + *
  • a `pkl.base` module property + *
  • a property accessible through `this` + *
* *

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 @@ private ExpressionNode doResolve(VirtualFrame frame) { 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 @@ private ExpressionNode doResolve(VirtualFrame frame) { 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 @@ private void checkConst(VmObjectLike currOwner, Member member, int levelsUp) { 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 (int i = descriptor.getNumberOfSlots() - 1; i >= 0; i--) { + var slotName = descriptor.getSlotName(i); + if (slotName == identifier1 || slotName == identifier2) { + return i; + } + } + return -1; + } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadAuxiliarySlotNode.java b/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadAuxiliarySlotNode.java deleted file mode 100644 index 9ed63c76b..000000000 --- a/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadAuxiliarySlotNode.java +++ /dev/null @@ -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); - } -} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadEnclosingAuxiliarySlotNode.java b/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadEnclosingAuxiliarySlotNode.java deleted file mode 100644 index 245d90c79..000000000 --- a/pkl-core/src/main/java/org/pkl/core/ast/frame/ReadEnclosingAuxiliarySlotNode.java +++ /dev/null @@ -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); - } -} diff --git a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java index 7e8a92d14..77303bc33 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/internal/ToStringNode.java @@ -80,7 +80,6 @@ protected InvokeMethodVirtualNode createInvokeNode() { Identifier.TO_STRING, new ExpressionNode[] {}, MemberLookupMode.EXPLICIT_RECEIVER, - false, null, null); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java index 5c30bcb32..d10f6752e 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction0Node.java @@ -33,12 +33,12 @@ protected Object evalDirect( 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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java index b44eca03a..063b71331 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction1Node.java @@ -77,13 +77,13 @@ protected Object evalDirect( 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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java index b8c9fb386..1f3acb22b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction2Node.java @@ -76,7 +76,7 @@ protected Object evalDirect( 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 @@ protected Object eval( 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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java index c88c66877..2e41ed1ff 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction3Node.java @@ -36,7 +36,7 @@ protected Object evalDirect( 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 @@ protected Object eval( @Cached("create()") IndirectCallNode callNode) { return callNode.call( - function.getCallTarget(), function.getThisValue(), function, false, arg1, arg2, arg3); + function.getCallTarget(), function.getThisValue(), function, arg1, arg2, arg3); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java index b716f2ace..6012558b3 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction4Node.java @@ -38,7 +38,7 @@ protected Object evalDirect( 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 @@ protected Object eval( @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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java index 8f699838a..affa9b476 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/lambda/ApplyVmFunction5Node.java @@ -39,7 +39,7 @@ protected Object evalDirect( 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 @@ protected Object eval( @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); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java index 876fd3fc3..f99a0f12c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ElementOrEntryNode.java @@ -49,7 +49,7 @@ protected Object evalListing( VirtualFrame frame, VmListing receiver, @Cached("create()") @Shared("callNode") IndirectCallNode callNode) { - var result = executeBody(frame); + var result = bodyNode.executeGeneric(frame); return VmUtils.shouldRunTypeCheck(frame) ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null) : result; @@ -60,7 +60,7 @@ protected Object evalMapping( VirtualFrame frame, VmMapping receiver, @Cached("create()") @Shared("callNode") IndirectCallNode callNode) { - var result = executeBody(frame); + var result = bodyNode.executeGeneric(frame); return VmUtils.shouldRunTypeCheck(frame) ? receiver.executeTypeCasts(result, VmUtils.getOwner(frame), callNode, null, null) : result; @@ -68,6 +68,6 @@ protected Object evalMapping( @Specialization protected Object evalDynamic(VirtualFrame frame, VmDynamic ignored) { - return executeBody(frame); + return bodyNode.executeGeneric(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java index 8adbbefc4..a9d9f1040 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/FunctionNode.java @@ -29,7 +29,6 @@ import org.pkl.core.ast.ExpressionNode; import org.pkl.core.ast.VmModifier; import org.pkl.core.ast.type.TypeNode; -import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.runtime.*; import org.pkl.core.util.CollectionUtils; import org.pkl.core.util.Nullable; @@ -44,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; @@ -106,45 +101,25 @@ public String getCallSignature() { @Override @ExplodeLoop - public Object execute(VirtualFrame frame) { + protected Object executeImpl(VirtualFrame frame) { var totalArgCount = frame.getArguments().length; if (totalArgCount != totalParamCount) { CompilerDirectives.transferToInterpreter(); throw wrongArgumentCount(totalArgCount - IMPLICIT_PARAM_COUNT); } - var isInIterable = (boolean) frame.getArguments()[2]; - try { - for (var i = 0; i < parameterTypeNodes.length; i++) { - var argument = frame.getArguments()[IMPLICIT_PARAM_COUNT + i]; - if (isInIterable) { - parameterTypeNodes[i].executeEagerlyAndSet(frame, argument); - } else { - parameterTypeNodes[i].executeAndSet(frame, argument); - } - } + for (var i = 0; i < parameterTypeNodes.length; i++) { + var argument = frame.getArguments()[IMPLICIT_PARAM_COUNT + i]; + parameterTypeNodes[i].executeAndSet(frame, argument); + } - var result = bodyNode.executeGeneric(frame); + var result = bodyNode.executeGeneric(frame); - if (checkedReturnTypeNode != null) { - return checkedReturnTypeNode.execute(frame, result); - } - - return result; - } catch (VmTypeMismatchException e) { - CompilerDirectives.transferToInterpreter(); - throw e.toVmException(); - } catch (StackOverflowError e) { - CompilerDirectives.transferToInterpreter(); - throw new VmStackOverflowException(e); - } catch (Exception e) { - CompilerDirectives.transferToInterpreter(); - if (e instanceof VmException) { - throw e; - } else { - throw exceptionBuilder().bug(e.getMessage()).withCause(e).build(); - } + if (checkedReturnTypeNode != null) { + return checkedReturnTypeNode.execute(frame, result); } + + return result; } public VmMap getParameterMirrors() { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ListingOrMappingTypeCastNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ListingOrMappingTypeCastNode.java index 16a327d79..6ac3e304c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ListingOrMappingTypeCastNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ListingOrMappingTypeCastNode.java @@ -15,13 +15,11 @@ */ package org.pkl.core.ast.member; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.PklRootNode; import org.pkl.core.ast.type.TypeNode; -import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.runtime.VmLanguage; import org.pkl.core.util.Nullable; @@ -53,12 +51,7 @@ public SourceSection getSourceSection() { } @Override - public Object execute(VirtualFrame frame) { - try { - return typeNode.execute(frame, frame.getArguments()[2]); - } catch (VmTypeMismatchException e) { - CompilerDirectives.transferToInterpreter(); - throw e.toVmException(); - } + protected Object executeImpl(VirtualFrame frame) { + return typeNode.execute(frame, frame.getArguments()[2]); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java index 5cfe2443d..7998bea8d 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/LocalTypedPropertyNode.java @@ -21,8 +21,6 @@ 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.VmException; import org.pkl.core.runtime.VmLanguage; import org.pkl.core.util.LateInit; import org.pkl.core.util.Nullable; @@ -58,25 +56,13 @@ public LocalTypedPropertyNode( } @Override - public Object execute(VirtualFrame frame) { - try { - if (typeNode == null) { - CompilerDirectives.transferToInterpreter(); - typeNode = insert(unresolvedTypeNode.execute(frame)); - unresolvedTypeNode = null; - } - var result = bodyNode.executeGeneric(frame); - return typeNode.execute(frame, result); - } catch (VmTypeMismatchException e) { + protected Object executeImpl(VirtualFrame frame) { + if (typeNode == null) { CompilerDirectives.transferToInterpreter(); - throw e.toVmException(); - } catch (Exception e) { - CompilerDirectives.transferToInterpreter(); - if (e instanceof VmException) { - throw e; - } else { - throw exceptionBuilder().bug(e.getMessage()).withCause(e).build(); - } + typeNode = insert(unresolvedTypeNode.execute(frame)); + unresolvedTypeNode = null; } + var result = bodyNode.executeGeneric(frame); + return typeNode.execute(frame, result); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/Member.java b/pkl-core/src/main/java/org/pkl/core/ast/member/Member.java index a881c939a..70d2a4b2c 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/Member.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/Member.java @@ -130,20 +130,4 @@ public final boolean isConstOrFixed() { 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. - * - *

This is {@code true} for {@code new {}} within: - * - *

{@code
-   * for (x in new Listing { new {} }) {
-   *                         ^^^^^^
-   *   // etc
-   * }
-   * }
- */ - public boolean isInIterable() { - return VmModifier.isInIterable(modifiers); - } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ModuleNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ModuleNode.java index 48beb3760..ea9d84ff2 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ModuleNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ModuleNode.java @@ -52,8 +52,8 @@ public String getName() { } @Override - public Object execute(VirtualFrame frame) { - var module = executeBody(frame, moduleNode); + protected Object executeImpl(VirtualFrame frame) { + var module = moduleNode.executeGeneric(frame); if (module instanceof VmClass vmClass) { return vmClass.getPrototype(); } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMethodNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMethodNode.java index 416a86e61..aa62321cd 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMethodNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/ObjectMethodNode.java @@ -59,7 +59,7 @@ public ObjectMethodNode( } @Override - public CallTarget execute(VirtualFrame frame) { + protected CallTarget executeImpl(VirtualFrame frame) { if (functionNode == null) { CompilerDirectives.transferToInterpreter(); diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java index 09bd3caab..40096e954 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/PropertyTypeNode.java @@ -15,7 +15,6 @@ */ package org.pkl.core.ast.member; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.frame.FrameDescriptor; import com.oracle.truffle.api.frame.VirtualFrame; @@ -24,7 +23,6 @@ import org.pkl.core.ast.PklRootNode; import org.pkl.core.ast.type.TypeNode; import org.pkl.core.ast.type.TypeNode.UnknownTypeNode; -import org.pkl.core.ast.type.VmTypeMismatchException; import org.pkl.core.runtime.*; import org.pkl.core.util.Nullable; @@ -61,37 +59,9 @@ public String getName() { return qualifiedPropertyName; } - private boolean isInIterable(VirtualFrame frame) { - var args = frame.getArguments(); - return args.length >= 4 && args[3] instanceof Boolean b && b; - } - @Override - public Object execute(VirtualFrame frame) { - try { - 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]); - } catch (VmTypeMismatchException e) { - CompilerDirectives.transferToInterpreter(); - throw e.toVmException(); - } catch (Exception e) { - CompilerDirectives.transferToInterpreter(); - if (e instanceof VmException) { - throw e; - } else { - throw exceptionBuilder().bug(e.getMessage()).withCause(e).build(); - } - } + protected Object executeImpl(VirtualFrame frame) { + return typeNode.execute(frame, frame.getArguments()[2]); } public @Nullable Object getDefaultValue() { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/SharedMemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/SharedMemberNode.java index 5237def42..cddb32088 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/SharedMemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/SharedMemberNode.java @@ -58,7 +58,7 @@ public SourceSection getHeaderSection() { } @Override - public Object execute(VirtualFrame frame) { - return executeBody(frame); + protected Object executeImpl(VirtualFrame frame) { + return bodyNode.executeGeneric(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/TypeCheckedPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/TypeCheckedPropertyNode.java index 08bc1b535..b7dd28743 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/TypeCheckedPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/TypeCheckedPropertyNode.java @@ -51,12 +51,11 @@ protected Object evalTypedObjectCached( @Cached("getProperty(cachedOwnerClass)") ClassProperty property, @Cached("createTypeCheckCallNode(property)") @Nullable DirectCallNode callNode) { - var result = executeBody(frame); + var result = bodyNode.executeGeneric(frame); // 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; @@ -66,7 +65,7 @@ protected Object evalTypedObjectCached( protected Object eval( VirtualFrame frame, VmObjectLike owner, @Cached("create()") IndirectCallNode callNode) { - var result = executeBody(frame); + var result = bodyNode.executeGeneric(frame); if (VmUtils.shouldRunTypeCheck(frame)) { var property = getProperty(owner.getVmClass()); @@ -76,8 +75,7 @@ protected Object eval( typeAnnNode.getCallTarget(), VmUtils.getReceiverOrNull(frame), property.getOwner(), - result, - member.isInIterable()); + result); } } @@ -86,7 +84,7 @@ protected Object eval( @Specialization protected Object eval(VirtualFrame frame, @SuppressWarnings("unused") VmDynamic owner) { - return executeBody(frame); + return bodyNode.executeGeneric(frame); } protected ClassProperty getProperty(VmClass ownerClass) { diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/TypedPropertyNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/TypedPropertyNode.java index 8677d02ba..208c2660e 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/TypedPropertyNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/TypedPropertyNode.java @@ -43,14 +43,11 @@ public TypedPropertyNode( } @Override - public Object execute(VirtualFrame frame) { - var propertyValue = executeBody(frame); + protected Object executeImpl(VirtualFrame frame) { + 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; } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/member/UntypedObjectMemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/member/UntypedObjectMemberNode.java index 777fbdb18..bd1f11b21 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/member/UntypedObjectMemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/member/UntypedObjectMemberNode.java @@ -32,7 +32,7 @@ public UntypedObjectMemberNode( } @Override - public Object execute(VirtualFrame frame) { - return executeBody(frame); + protected Object executeImpl(VirtualFrame frame) { + return bodyNode.executeGeneric(frame); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/repl/ResolveClassMemberNode.java b/pkl-core/src/main/java/org/pkl/core/ast/repl/ResolveClassMemberNode.java index d8ced2d53..c9d587c7b 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/repl/ResolveClassMemberNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/repl/ResolveClassMemberNode.java @@ -49,7 +49,7 @@ public String getName() { } @Override - public Object execute(VirtualFrame frame) { + protected Object executeImpl(VirtualFrame frame) { return unresolvedNode.execute(frame, clazz); } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java index 652b9dcf0..8ecb53a14 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/IdentityMixinNode.java @@ -20,7 +20,6 @@ import com.oracle.truffle.api.frame.VirtualFrame; import com.oracle.truffle.api.source.SourceSection; import org.pkl.core.ast.PklRootNode; -import org.pkl.core.runtime.VmException; import org.pkl.core.runtime.VmLanguage; import org.pkl.core.util.Nullable; @@ -53,32 +52,19 @@ public String getName() { } @Override - public Object execute(VirtualFrame frame) { + 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(); } - - try { - var argument = arguments[3]; - if (argumentTypeNode != null) { - return argumentTypeNode.execute(frame, argument); - } - return argument; - } catch (VmTypeMismatchException e) { - CompilerDirectives.transferToInterpreter(); - throw e.toVmException(); - } catch (Exception e) { - CompilerDirectives.transferToInterpreter(); - if (e instanceof VmException) { - throw e; - } else { - throw exceptionBuilder().bug(e.getMessage()).withCause(e).build(); - } + var argument = arguments[2]; + if (argumentTypeNode != null) { + return argumentTypeNode.execute(frame, argument); } + return argument; } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeConstraintNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeConstraintNode.java index fbb6dcc9b..d6cd316f6 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeConstraintNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeConstraintNode.java @@ -25,7 +25,6 @@ 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 @@ private void initConstraintSlot(VirtualFrame frame) { 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); } } } diff --git a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java index 3c8e6b900..1d2af5744 100644 --- a/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java +++ b/pkl-core/src/main/java/org/pkl/core/ast/type/TypeNode.java @@ -95,14 +95,6 @@ public Object executeEagerly(VirtualFrame frame, Object value) { return execute(frame, value); } - /** - * Checks if {@code value} conforms to this type. - * - *

If {@code value} is conforming, sets {@code slot} to {@code value}. Otherwise, throws a - * {@link VmTypeMismatchException}. - */ - public abstract Object executeEagerlyAndSet(VirtualFrame frame, Object value); - // method arguments are used when default value contains a root node public @Nullable Object createDefaultValue( VmLanguage language, @@ -226,11 +218,6 @@ public final Object executeAndSet(VirtualFrame frame, Object value) { 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 final Object executeAndSet(VirtualFrame frame, Object value) { 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 final Object executeAndSet(VirtualFrame frame, Object value) { 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 Object executeAndSet(VirtualFrame frame, Object value) { 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 Object executeAndSet(VirtualFrame frame, Object value) { } } - /** 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 Object executeAndSet(VirtualFrame frame, Object value) { 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 Object executeAndSet(VirtualFrame frame, Object value) { } } - @Override - public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { - return executeAndSet(frame, value); - } - @Override public VmClass getVmClass() { return BaseModule.getNumberClass(); @@ -2813,11 +2753,6 @@ public Object executeAndSet(VirtualFrame frame, Object value) { 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 Object executeAndSet(VirtualFrame frame, Object value) { return value; } - @Override - public Object executeEagerlyAndSet(VirtualFrame frame, Object value) { - return executeAndSet(frame, value); - } - @Override public VmClass getVmClass() { return BaseModule.getBooleanClass(); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java b/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java index 89f5696fe..d42ab0422 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/Identifier.java @@ -141,9 +141,6 @@ public final class Identifier implements Comparable { // 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) { diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java index 95dfbdb2e..55cb08745 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmFunction.java @@ -55,7 +55,7 @@ public int getParameterCount() { // 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 String applyString(Object arg1) { // 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( diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmObjectLike.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmObjectLike.java index 0330f04f0..577327926 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmObjectLike.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmObjectLike.java @@ -23,8 +23,8 @@ 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 boolean isModuleObject() { } /** - * 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(); diff --git a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java index 079aca07b..080137455 100644 --- a/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java +++ b/pkl-core/src/main/java/org/pkl/core/runtime/VmUtils.java @@ -47,6 +47,7 @@ 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 static Object doReadMember( return result; } + /** + * Copies `numberOfLocalsToCopy` locals from `sourceFrame`, starting at `firstSourceSlot`, to + * `targetFrame`, starting at `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 static ObjectMember createLocalObjectProperty( } 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 static Object evaluateExpression( 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 diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java index 1faf2aa52..534269498 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/base/FunctionNodes.java @@ -31,12 +31,11 @@ public abstract static class applyToList extends ExternalMethod1Node { 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; } diff --git a/pkl-core/src/main/java/org/pkl/core/stdlib/benchmark/MicrobenchmarkNodes.java b/pkl-core/src/main/java/org/pkl/core/stdlib/benchmark/MicrobenchmarkNodes.java index c8272b1f4..757e41444 100644 --- a/pkl-core/src/main/java/org/pkl/core/stdlib/benchmark/MicrobenchmarkNodes.java +++ b/pkl-core/src/main/java/org/pkl/core/stdlib/benchmark/MicrobenchmarkNodes.java @@ -17,7 +17,6 @@ import static org.pkl.core.stdlib.benchmark.BenchmarkUtils.runBenchmark; -import com.oracle.truffle.api.CompilerDirectives; import com.oracle.truffle.api.CompilerDirectives.TruffleBoundary; import com.oracle.truffle.api.dsl.Specialization; import com.oracle.truffle.api.frame.FrameDescriptor; @@ -29,7 +28,6 @@ import org.pkl.core.ast.internal.BlackholeNode; import org.pkl.core.ast.internal.BlackholeNodeGen; import org.pkl.core.runtime.Identifier; -import org.pkl.core.runtime.VmException; import org.pkl.core.runtime.VmLanguage; import org.pkl.core.runtime.VmTyped; import org.pkl.core.runtime.VmUtils; @@ -83,23 +81,14 @@ public SourceSection getSourceSection() { } @Override - public @Nullable Object execute(VirtualFrame frame) { - try { - var repetitions = (long) frame.getArguments()[2]; - for (long i = 0; i < repetitions; i++) { - blackholeNode.executeGeneric(frame); - } - LoopNode.reportLoopCount( - this, repetitions > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) repetitions); - return null; - } catch (Exception e) { - CompilerDirectives.transferToInterpreter(); - if (e instanceof VmException) { - throw e; - } else { - throw exceptionBuilder().bug(e.getMessage()).withCause(e).build(); - } + protected @Nullable Object executeImpl(VirtualFrame frame) { + var repetitions = (long) frame.getArguments()[2]; + for (long i = 0; i < repetitions; i++) { + blackholeNode.executeGeneric(frame); } + LoopNode.reportLoopCount( + this, repetitions > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int) repetitions); + return null; } } } diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/forGeneratorDuplicateParams2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/errors/forGeneratorDuplicateParams2.pkl deleted file mode 100644 index 515636ab0..000000000 --- a/pkl-core/src/test/files/LanguageSnippetTests/input/errors/forGeneratorDuplicateParams2.pkl +++ /dev/null @@ -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 - } - } -} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl new file mode 100644 index 000000000..d042836a0 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorInFunctionBody.pkl @@ -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") diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl new file mode 100644 index 000000000..edf2ff776 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorNestedReference2.pkl @@ -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 + } + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl new file mode 100644 index 000000000..1a90b7125 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/input/generators/forGeneratorVariableShadowing.pkl @@ -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) + } + } + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/forGeneratorDuplicateParams2.err b/pkl-core/src/test/files/LanguageSnippetTests/output/errors/forGeneratorDuplicateParams2.err deleted file mode 100644 index 92fbacfd1..000000000 --- a/pkl-core/src/test/files/LanguageSnippetTests/output/errors/forGeneratorDuplicateParams2.err +++ /dev/null @@ -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) diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf new file mode 100644 index 000000000..d31c4e115 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorInFunctionBody.pcf @@ -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") +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf new file mode 100644 index 000000000..0fdfcde7a --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorNestedReference2.pcf @@ -0,0 +1,11 @@ +bar {} +res1 { + new { + 1 + } +} +res2 { + new { + 1 + } +} diff --git a/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf new file mode 100644 index 000000000..a5eb19bb2 --- /dev/null +++ b/pkl-core/src/test/files/LanguageSnippetTests/output/generators/forGeneratorVariableShadowing.pcf @@ -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") + } + } +}