mirror of
https://github.com/apple/pkl.git
synced 2026-06-12 08:34:27 +02:00
Improve handling of frame slots (#1634)
This makes various improvements to the handling of frame slot vars, and includes some bug fixes introduced by https://github.com/apple/pkl/pull/1622 * Refactor SymbolTable to track for-generator and parameter slots in each scope * Execute let expressions in their own root node in some places * Unify how frame slots are managed; they are all represented as `FrameSlotVariable`, created in `AstBuilder`, and passed into `SymbolTable`. * Fix how let expressions are executed in custom this scopes (introduce a new root node when needed)
This commit is contained in:
@@ -52,9 +52,10 @@ import org.pkl.core.ast.builder.MethodResolution.ImplicitThisMethod;
|
||||
import org.pkl.core.ast.builder.MethodResolution.LexicalMethod;
|
||||
import org.pkl.core.ast.builder.SymbolTable.AnnotationScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.ClassScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.LetExpressionScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.ModuleScope;
|
||||
import org.pkl.core.ast.builder.SymbolTable.ObjectScope;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ForGeneratorOrLetVariable;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ForGeneratorVariableOrLetBinding;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ImplicitBaseProperty;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ImplicitThisProperty;
|
||||
import org.pkl.core.ast.builder.VariableResolution.LexicalProperty;
|
||||
@@ -121,6 +122,7 @@ import org.pkl.core.ast.expression.member.ReadLocalPropertyNode;
|
||||
import org.pkl.core.ast.expression.member.ReadPropertyNodeGen;
|
||||
import org.pkl.core.ast.expression.member.ReadSuperEntryNode;
|
||||
import org.pkl.core.ast.expression.member.ReadSuperPropertyNode;
|
||||
import org.pkl.core.ast.expression.primary.ExecuteCustomThisWithRootNode;
|
||||
import org.pkl.core.ast.expression.primary.GetEnclosingReceiverNode;
|
||||
import org.pkl.core.ast.expression.primary.GetMemberKeyNode;
|
||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
||||
@@ -179,6 +181,7 @@ import org.pkl.core.module.ResolvedModuleKey;
|
||||
import org.pkl.core.packages.PackageLoadError;
|
||||
import org.pkl.core.runtime.BaseModule;
|
||||
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||
import org.pkl.core.runtime.FrameSlotVariable;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.ModuleResolver;
|
||||
import org.pkl.core.runtime.VmBytes;
|
||||
@@ -417,14 +420,33 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
scope -> {
|
||||
var exprs = type.getExprs();
|
||||
var constraints = new TypeConstraintNode[exprs.size()];
|
||||
for (int i = 0; i < constraints.length; i++) {
|
||||
for (var i = 0; i < constraints.length; i++) {
|
||||
var currentFrameDescriptorSize = scope.frameDescriptorBuilder.getSize();
|
||||
var expr = visitExpr(exprs.get(i));
|
||||
var writesFrameSlotVars =
|
||||
scope.frameDescriptorBuilder.getSize() > currentFrameDescriptorSize;
|
||||
// if a constraint expression writes to frame slots (only known case: is a `let` expr),
|
||||
// create a new root node and execute the constraint within this root node
|
||||
// e.g. String(let (x = this) x.length > 5)
|
||||
if (writesFrameSlotVars) {
|
||||
expr = getExprWithinCustomThis(scope, expr);
|
||||
}
|
||||
constraints[i] = TypeConstraintNodeGen.create(expr.getSourceSection(), expr);
|
||||
}
|
||||
return new Constrained(createSourceSection(type), language, childNode, constraints);
|
||||
});
|
||||
}
|
||||
|
||||
private ExpressionNode getExprWithinCustomThis(SymbolTable.Scope scope, ExpressionNode expr) {
|
||||
return new ExecuteCustomThisWithRootNode(
|
||||
expr.getSourceSection(),
|
||||
expr,
|
||||
scope.frameDescriptorBuilder.build(),
|
||||
scope.getQualifiedName(),
|
||||
scope.forGeneratorSlots,
|
||||
scope.parameterSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public UnresolvedTypeNode visitUnionType(UnionType type) {
|
||||
var elementTypes = type.getTypes();
|
||||
@@ -682,13 +704,13 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
MemberLookupMode.IMPLICIT_LEXICAL,
|
||||
needsConst,
|
||||
p.levelsUp() == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(p.levelsUp()));
|
||||
} else if (resolution instanceof ForGeneratorOrLetVariable p) {
|
||||
} else if (resolution instanceof ForGeneratorVariableOrLetBinding p) {
|
||||
// Parameters can possibly write to frame slots actually in a frame that is one level
|
||||
// higher than what we can tell at parse time. However, let exprs and for generator variables
|
||||
// always write to frame slots in the same frame.
|
||||
//
|
||||
// function foo(bar) = new Mixin {
|
||||
// [bar] = 1 <--- actually 1 level, not 0
|
||||
// res = bar <--- actually 1 level, not 0
|
||||
// for (elem in qux) {
|
||||
// elem <--- actually 0 level
|
||||
// }
|
||||
@@ -1096,26 +1118,30 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
var sourceSection = createSourceSection(letExpr);
|
||||
var parameter = letExpr.getParameter();
|
||||
UnresolvedTypeNode typeNode = null;
|
||||
String binding = null;
|
||||
var slot = -1;
|
||||
@Nullable FrameSlotVariable binding = null;
|
||||
var frameDescriptorBuilder = symbolTable.getCurrentScope().frameDescriptorBuilder;
|
||||
if (parameter instanceof TypedIdentifier par) {
|
||||
typeNode = visitTypeAnnotation(par.getTypeAnnotation());
|
||||
slot =
|
||||
binding =
|
||||
frameDescriptorBuilder.addSlot(
|
||||
FrameSlotKind.Illegal, toIdentifier(par.getIdentifier().getValue()), null);
|
||||
binding = par.getIdentifier().getValue();
|
||||
FrameSlotKind.Illegal,
|
||||
toIdentifier(par.getIdentifier().getValue()),
|
||||
LetExpressionScope.LET_BINDING_SLOT);
|
||||
}
|
||||
var bindingExpr = visitExpr(letExpr.getBindingExpr());
|
||||
var t = typeNode;
|
||||
var s = slot;
|
||||
var b = binding;
|
||||
return symbolTable.enterLetExpression(
|
||||
binding,
|
||||
slot,
|
||||
scope -> {
|
||||
var bodyExpr = visitExpr(letExpr.getExpr());
|
||||
return LetExprNodeGen.create(
|
||||
sourceSection, scope.getQualifiedName(), t, bodyExpr, s, bindingExpr);
|
||||
sourceSection,
|
||||
scope.getQualifiedName(),
|
||||
t,
|
||||
bodyExpr,
|
||||
b == null ? -1 : b.slot(),
|
||||
bindingExpr);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1123,9 +1149,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
public ExpressionNode visitFunctionLiteralExpr(FunctionLiteralExpr expr) {
|
||||
var sourceSection = createSourceSection(expr);
|
||||
var params = expr.getParameterList();
|
||||
var descriptorBuilder = createFrameDescriptorBuilder(params);
|
||||
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(params);
|
||||
var paramCount = params.getParameters().size();
|
||||
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||
var bindings = descriptorBuilderAndBindings.second;
|
||||
if (paramCount > 5) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("tooManyFunctionParameters")
|
||||
@@ -1133,8 +1160,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
.build();
|
||||
}
|
||||
|
||||
var bindings = getParameterNames(params);
|
||||
|
||||
var isCustomThisScope = symbolTable.getCurrentScope().isCustomThisScope();
|
||||
|
||||
return symbolTable.enterLambda(
|
||||
@@ -1281,7 +1306,18 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
public GeneratorMemberNode visitMemberPredicate(MemberPredicate ctx) {
|
||||
var keyNode =
|
||||
symbolTable.enterEagerGenerator(
|
||||
(scp) -> symbolTable.enterCustomThisScope(scope -> visitExpr(ctx.getPred())));
|
||||
(scp) ->
|
||||
symbolTable.enterCustomThisScope(
|
||||
scope -> {
|
||||
var currentFrameDescriptorSize = scope.frameDescriptorBuilder.getSize();
|
||||
var expr = visitExpr(ctx.getPred());
|
||||
var writesFrameSlotVars =
|
||||
scope.frameDescriptorBuilder.getSize() > currentFrameDescriptorSize;
|
||||
if (writesFrameSlotVars) {
|
||||
return getExprWithinCustomThis(scope, expr);
|
||||
}
|
||||
return expr;
|
||||
}));
|
||||
var member =
|
||||
doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.getExpr(), ctx.getBodyList());
|
||||
var isFrameStored =
|
||||
@@ -1339,82 +1375,81 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return doVisitGeneratorMemberNodes(body.getMembers());
|
||||
}
|
||||
|
||||
private @Nullable FrameSlotVariable makeBinding(
|
||||
org.pkl.parser.syntax.@Nullable Parameter parameter) {
|
||||
if (!(parameter instanceof TypedIdentifier typedIdentifier)) {
|
||||
return null;
|
||||
}
|
||||
var name = typedIdentifier.getIdentifier().getValue();
|
||||
return symbolTable
|
||||
.getCurrentScope()
|
||||
.frameDescriptorBuilder
|
||||
.addSlot(FrameSlotKind.Illegal, toIdentifier(name), null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public GeneratorMemberNode visitForGenerator(ForGenerator ctx) {
|
||||
|
||||
var keyParameter = ctx.getP2() == null ? null : ctx.getP1();
|
||||
var valueParameter = ctx.getP2() == null ? ctx.getP1() : ctx.getP2();
|
||||
TypedIdentifier keyTypedIdentifier = null;
|
||||
if (keyParameter instanceof TypedIdentifier ti) keyTypedIdentifier = ti;
|
||||
TypedIdentifier valueTypedIdentifier = null;
|
||||
if (valueParameter instanceof TypedIdentifier ti) valueTypedIdentifier = ti;
|
||||
|
||||
var params = new ArrayList<String>();
|
||||
if (ctx.getP1() instanceof TypedIdentifier ti) {
|
||||
params.add(ti.getIdentifier().getValue());
|
||||
}
|
||||
if (ctx.getP2() != null) {
|
||||
if (ctx.getP2() instanceof TypedIdentifier ti) {
|
||||
params.add(ti.getIdentifier().getValue());
|
||||
}
|
||||
}
|
||||
|
||||
var keyBinding = makeBinding(keyParameter);
|
||||
var valueBinding = makeBinding(valueParameter);
|
||||
var keyIdentifier =
|
||||
keyTypedIdentifier == null
|
||||
? null
|
||||
: toIdentifier(keyTypedIdentifier.getIdentifier().getValue());
|
||||
keyParameter instanceof TypedIdentifier keyTypedIdentifier ? keyTypedIdentifier : null;
|
||||
var valueIdentifier =
|
||||
valueTypedIdentifier == null
|
||||
? null
|
||||
: toIdentifier(valueTypedIdentifier.getIdentifier().getValue());
|
||||
if (valueIdentifier != null && valueIdentifier == keyIdentifier) {
|
||||
valueParameter instanceof TypedIdentifier valueTypedIdentifier
|
||||
? valueTypedIdentifier
|
||||
: null;
|
||||
if (keyIdentifier != null
|
||||
&& valueIdentifier != null
|
||||
&& keyIdentifier
|
||||
.getIdentifier()
|
||||
.getValue()
|
||||
.equals(valueIdentifier.getIdentifier().getValue())) {
|
||||
throw exceptionBuilder()
|
||||
.evalError("duplicateDefinition", valueIdentifier)
|
||||
.withSourceSection(createSourceSection(valueTypedIdentifier.getIdentifier()))
|
||||
.evalError("duplicateDefinition", valueIdentifier.getIdentifier().getValue())
|
||||
.withSourceSection(createSourceSection(valueIdentifier))
|
||||
.build();
|
||||
}
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
var generatorDescriptorBuilder = currentScope.newFrameDescriptorBuilder();
|
||||
var keySlot = -1;
|
||||
var valueSlot = -1;
|
||||
if (keyIdentifier != null) {
|
||||
keySlot = generatorDescriptorBuilder.addSlot(FrameSlotKind.Illegal, keyIdentifier, null);
|
||||
}
|
||||
if (valueIdentifier != null) {
|
||||
valueSlot = generatorDescriptorBuilder.addSlot(FrameSlotKind.Illegal, valueIdentifier, null);
|
||||
}
|
||||
|
||||
var unresolvedKeyTypeNode =
|
||||
keyTypedIdentifier == null
|
||||
? null
|
||||
: visitTypeAnnotation(keyTypedIdentifier.getTypeAnnotation());
|
||||
keyIdentifier == null ? null : visitTypeAnnotation(keyIdentifier.getTypeAnnotation());
|
||||
var unresolvedValueTypeNode =
|
||||
valueTypedIdentifier == null
|
||||
? null
|
||||
: visitTypeAnnotation(valueTypedIdentifier.getTypeAnnotation());
|
||||
valueIdentifier == null ? null : visitTypeAnnotation(valueIdentifier.getTypeAnnotation());
|
||||
// if possible, initialize immediately to avoid later insert
|
||||
var keyTypeNode =
|
||||
unresolvedKeyTypeNode == null && keySlot != -1
|
||||
unresolvedKeyTypeNode == null && keyBinding != null
|
||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(keySlot)
|
||||
.initWriteSlotNode(keyBinding.slot())
|
||||
: null;
|
||||
// if possible, initialize immediately to avoid later insert
|
||||
var valueTypeNode =
|
||||
unresolvedValueTypeNode == null && valueSlot != -1
|
||||
unresolvedValueTypeNode == null && valueBinding != null
|
||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||
.initWriteSlotNode(valueSlot)
|
||||
.initWriteSlotNode(valueBinding.slot())
|
||||
: null;
|
||||
var iterableNode = symbolTable.enterEagerGenerator(scope -> visitExpr(ctx.getExpr()));
|
||||
var memberNodes =
|
||||
symbolTable.enterForGenerator(
|
||||
params, generatorDescriptorBuilder, scope -> doVisitForWhenBody(ctx.getBody()));
|
||||
return GeneratorForNodeGen.create(
|
||||
createSourceSection(ctx),
|
||||
generatorDescriptorBuilder.build(),
|
||||
iterableNode,
|
||||
unresolvedKeyTypeNode,
|
||||
unresolvedValueTypeNode,
|
||||
memberNodes,
|
||||
keyTypeNode,
|
||||
valueTypeNode);
|
||||
var outerScope = symbolTable.getCurrentScope();
|
||||
return symbolTable.enterForGenerator(
|
||||
keyBinding,
|
||||
valueBinding,
|
||||
symbolTable.getCurrentScope().frameDescriptorBuilder,
|
||||
scope -> {
|
||||
var memberNodes = doVisitForWhenBody(ctx.getBody());
|
||||
return GeneratorForNodeGen.create(
|
||||
createSourceSection(ctx),
|
||||
scope.frameDescriptorBuilder.build(),
|
||||
iterableNode,
|
||||
unresolvedKeyTypeNode,
|
||||
unresolvedValueTypeNode,
|
||||
memberNodes,
|
||||
keyTypeNode,
|
||||
valueTypeNode,
|
||||
keyBinding == null ? -1 : keyBinding.slot(),
|
||||
valueBinding == null ? -1 : valueBinding.slot(),
|
||||
outerScope.getForGeneratorSlots(),
|
||||
scope.getParameterSlots());
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -1937,10 +1972,11 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
var bodyContext = entry.getExpr();
|
||||
var paramListCtx = entry.getParameterList();
|
||||
var descriptorBuilder = createFrameDescriptorBuilder(paramListCtx);
|
||||
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(paramListCtx);
|
||||
var paramCount = paramListCtx.getParameters().size();
|
||||
|
||||
var bindings = getParameterNames(paramListCtx);
|
||||
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||
var bindings = descriptorBuilderAndBindings.second;
|
||||
|
||||
var annotations = doVisitAnnotations(entry.getAnnotations(), methodName);
|
||||
|
||||
@@ -2221,8 +2257,14 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
private ExpressionNode doVisitObjectBody(ObjectBody body, ExpressionNode parentNode) {
|
||||
var parametersDescriptorAndBindings = createFrameDescriptorBuilderAndSlotVariables(body);
|
||||
var bindings =
|
||||
parametersDescriptorAndBindings == null
|
||||
? new FrameSlotVariable[0]
|
||||
: parametersDescriptorAndBindings.second;
|
||||
|
||||
return symbolTable.enterObjectScope(
|
||||
body,
|
||||
bindings,
|
||||
(scope) -> {
|
||||
addObjectNamesToScope(scope, body);
|
||||
var objectMembers = body.getMembers();
|
||||
@@ -2232,7 +2274,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
var sourceSection = createSourceSection(body.parent());
|
||||
|
||||
var parametersDescriptorBuilder = createFrameDescriptorBuilder(body);
|
||||
var parametersDescriptor =
|
||||
parametersDescriptorAndBindings == null
|
||||
? null
|
||||
: parametersDescriptorAndBindings.first.build();
|
||||
var parameterTypes = doVisitParameterTypes(body);
|
||||
|
||||
var members = EconomicMaps.<Object, ObjectMember>create();
|
||||
@@ -2278,8 +2323,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
var parametersDescriptor =
|
||||
parametersDescriptorBuilder == null ? null : parametersDescriptorBuilder.build();
|
||||
if (!elements.isEmpty()) {
|
||||
if (isConstantKeyNodes) { // true if zero key nodes
|
||||
addConstantEntries(members, keyNodes, values);
|
||||
@@ -2516,8 +2559,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
}
|
||||
|
||||
private ObjectMember doVisitObjectElement(ObjectElement element) {
|
||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return symbolTable.enterEntry(
|
||||
var outerScope = symbolTable.getCurrentScope();
|
||||
var isForGeneratorScope = outerScope.isForGeneratorScope();
|
||||
return symbolTable.enterEntryOrElement(
|
||||
null,
|
||||
scope -> {
|
||||
var elementNode = visitExpr(element.getExpr());
|
||||
@@ -2535,7 +2579,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
member.initConstantValue(constantNode);
|
||||
} else {
|
||||
if (isForGeneratorScope) {
|
||||
elementNode = new RestoreForBindingsNode(elementNode);
|
||||
elementNode =
|
||||
new RestoreForBindingsNode(
|
||||
elementNode, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
@@ -2586,9 +2632,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
var methodName = org.pkl.core.runtime.Identifier.method(identifier.getValue(), true);
|
||||
|
||||
var frameDescriptorBuilder = createFrameDescriptorBuilder(paramList);
|
||||
var frameDescriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(paramList);
|
||||
|
||||
var bindings = getParameterNames(paramList);
|
||||
var frameDescriptorBuilder = frameDescriptorBuilderAndBindings.first;
|
||||
var bindings = frameDescriptorBuilderAndBindings.second;
|
||||
|
||||
return symbolTable.enterMethod(
|
||||
methodName,
|
||||
@@ -2629,7 +2676,8 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
|
||||
private GeneratorObjectLiteralNode doVisitGeneratorObjectBody(
|
||||
ObjectBody body, ExpressionNode parentNode) {
|
||||
var parametersDescriptor = createFrameDescriptorBuilder(body);
|
||||
var parametersDescriptorBuilderAndFrameSlotVariables =
|
||||
createFrameDescriptorBuilderAndSlotVariables(body);
|
||||
var parameterTypes = doVisitParameterTypes(body);
|
||||
var memberNodes = doVisitGeneratorMemberNodes(body.getMembers());
|
||||
var currentScope = symbolTable.getCurrentScope();
|
||||
@@ -2639,7 +2687,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
language,
|
||||
currentScope.getQualifiedName(),
|
||||
currentScope.isCustomThisScope(),
|
||||
parametersDescriptor == null ? null : parametersDescriptor.build(),
|
||||
parametersDescriptorBuilderAndFrameSlotVariables == null
|
||||
? null
|
||||
: parametersDescriptorBuilderAndFrameSlotVariables.first.build(),
|
||||
parameterTypes,
|
||||
memberNodes,
|
||||
parentNode);
|
||||
@@ -2838,8 +2888,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
ExpressionNode keyNode,
|
||||
@Nullable Expr valueCtx,
|
||||
@Nullable List<? extends ObjectBody> objectBodyCtxs) {
|
||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
||||
return symbolTable.enterEntry(
|
||||
var outerScope = symbolTable.getCurrentScope();
|
||||
var isForGeneratorScope = outerScope.isForGeneratorScope();
|
||||
return symbolTable.enterEntryOrElement(
|
||||
keyNode,
|
||||
scope -> {
|
||||
var modifier = VmModifier.ENTRY;
|
||||
@@ -2856,7 +2907,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
member.initConstantValue(constantNode);
|
||||
} else {
|
||||
if (isForGeneratorScope) {
|
||||
valueNode = new RestoreForBindingsNode(valueNode);
|
||||
valueNode =
|
||||
new RestoreForBindingsNode(
|
||||
valueNode, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
@@ -2869,7 +2922,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
objectBodyCtxs,
|
||||
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
||||
if (isForGeneratorScope) {
|
||||
objectBody = new RestoreForBindingsNode(objectBody);
|
||||
objectBody =
|
||||
new RestoreForBindingsNode(
|
||||
objectBody, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||
}
|
||||
member.initMemberNode(
|
||||
ElementOrEntryNodeGen.create(
|
||||
@@ -2903,39 +2958,34 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
||||
return needsConst;
|
||||
}
|
||||
|
||||
private static List<String> getParameterNames(ParameterList parameterList) {
|
||||
var result = new ArrayList<String>(parameterList.getParameters().size());
|
||||
for (var param : parameterList.getParameters()) {
|
||||
var name = param instanceof TypedIdentifier id ? id.getIdentifier().getValue() : "_";
|
||||
result.add(name);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
private FrameDescriptorBuilder createFrameDescriptorBuilder(ParameterList params) {
|
||||
var builder = new FrameDescriptorBuilder(params.getParameters().size());
|
||||
for (var param : params.getParameters()) {
|
||||
org.pkl.core.runtime.Identifier identifier = null;
|
||||
private FrameSlotVariable[] getSlotVariables(
|
||||
List<org.pkl.parser.syntax.Parameter> params, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
var slotVariables = new FrameSlotVariable[params.size()];
|
||||
for (var i = 0; i < params.size(); i++) {
|
||||
var param = params.get(i);
|
||||
org.pkl.core.runtime.Identifier identifier;
|
||||
if (param instanceof TypedIdentifier typedIdentifier) {
|
||||
identifier = toIdentifier(typedIdentifier.getIdentifier().getValue());
|
||||
} else {
|
||||
identifier = org.pkl.core.runtime.Identifier.ILLEGAL;
|
||||
}
|
||||
builder.addSlot(FrameSlotKind.Illegal, identifier, null);
|
||||
slotVariables[i] = frameDescriptorBuilder.addSlot(FrameSlotKind.Illegal, identifier, null);
|
||||
}
|
||||
return builder;
|
||||
return slotVariables;
|
||||
}
|
||||
|
||||
private FrameDescriptor.@Nullable Builder createFrameDescriptorBuilder(ObjectBody body) {
|
||||
private Pair<FrameDescriptorBuilder, FrameSlotVariable[]>
|
||||
createFrameDescriptorBuilderAndSlotVariables(ParameterList params) {
|
||||
var builder = new FrameDescriptorBuilder(params.getParameters().size());
|
||||
return Pair.of(builder, getSlotVariables(params.getParameters(), builder));
|
||||
}
|
||||
|
||||
private @Nullable Pair<@Nullable FrameDescriptorBuilder, FrameSlotVariable[]>
|
||||
createFrameDescriptorBuilderAndSlotVariables(ObjectBody body) {
|
||||
if (body.getParameters().isEmpty()) return null;
|
||||
|
||||
var builder = FrameDescriptor.newBuilder(body.getParameters().size());
|
||||
for (var param : body.getParameters()) {
|
||||
org.pkl.core.runtime.Identifier identifier = null;
|
||||
if (param instanceof TypedIdentifier typedIdentifier) {
|
||||
identifier = toIdentifier(typedIdentifier.getIdentifier().getValue());
|
||||
}
|
||||
builder.addSlot(FrameSlotKind.Illegal, identifier, null);
|
||||
}
|
||||
return builder;
|
||||
var builder = new FrameDescriptorBuilder(body.getParameters().size());
|
||||
return Pair.of(builder, getSlotVariables(body.getParameters(), builder));
|
||||
}
|
||||
|
||||
private void checkNotInsideForGenerator(Node ctx, String errorMessageKey) {
|
||||
|
||||
@@ -15,6 +15,8 @@
|
||||
*/
|
||||
package org.pkl.core.ast.builder;
|
||||
|
||||
import static org.pkl.core.util.ArrayUtils.EMPTY_INT_ARRAY;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import java.util.*;
|
||||
import java.util.function.Function;
|
||||
@@ -26,19 +28,21 @@ import org.pkl.core.ast.VmModifier;
|
||||
import org.pkl.core.ast.builder.MethodResolution.ImplicitBaseMethod;
|
||||
import org.pkl.core.ast.builder.MethodResolution.ImplicitThisMethod;
|
||||
import org.pkl.core.ast.builder.MethodResolution.LexicalMethod;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ForGeneratorOrLetVariable;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ForGeneratorVariableOrLetBinding;
|
||||
import org.pkl.core.ast.builder.VariableResolution.ImplicitBaseProperty;
|
||||
import org.pkl.core.ast.builder.VariableResolution.LexicalProperty;
|
||||
import org.pkl.core.ast.builder.VariableResolution.Parameter;
|
||||
import org.pkl.core.ast.member.ObjectMember;
|
||||
import org.pkl.core.runtime.BaseModuleMembers;
|
||||
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||
import org.pkl.core.runtime.FrameSlotVariable;
|
||||
import org.pkl.core.runtime.Identifier;
|
||||
import org.pkl.core.runtime.ModuleInfo;
|
||||
import org.pkl.core.runtime.VmDataSize;
|
||||
import org.pkl.core.runtime.VmDuration;
|
||||
import org.pkl.core.util.ArrayUtils;
|
||||
import org.pkl.core.util.LateInit;
|
||||
import org.pkl.parser.Lexer;
|
||||
import org.pkl.parser.syntax.ObjectBody;
|
||||
|
||||
public final class SymbolTable {
|
||||
|
||||
@@ -94,7 +98,7 @@ public final class SymbolTable {
|
||||
public <T> T enterMethod(
|
||||
Identifier name,
|
||||
ConstLevel constLevel,
|
||||
List<String> bindings,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters,
|
||||
Function<MethodScope, T> nodeFactory) {
|
||||
@@ -115,17 +119,22 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public <T> T enterForGenerator(
|
||||
List<String> params,
|
||||
@Nullable FrameSlotVariable keyBinding,
|
||||
@Nullable FrameSlotVariable valueBinding,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
Function<ForGeneratorScope, T> nodeFactory) {
|
||||
return doEnter(
|
||||
new ForGeneratorScope(
|
||||
currentScope, currentScope.qualifiedName, params, frameDescriptorBuilder),
|
||||
currentScope,
|
||||
currentScope.qualifiedName,
|
||||
keyBinding,
|
||||
valueBinding,
|
||||
frameDescriptorBuilder),
|
||||
nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterLambda(
|
||||
List<String> bindings,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
Function<LambdaScope, T> nodeFactory) {
|
||||
|
||||
@@ -144,7 +153,7 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public <T> T enterLetExpression(
|
||||
@Nullable String binding, int slot, Function<LetExpressionScope, T> nodeFactory) {
|
||||
@Nullable FrameSlotVariable binding, Function<LetExpressionScope, T> nodeFactory) {
|
||||
|
||||
// flatten names of let exprs inside other let exprs for presentation purposes
|
||||
var parentScope = currentScope;
|
||||
@@ -154,7 +163,7 @@ public final class SymbolTable {
|
||||
|
||||
assert parentScope != null;
|
||||
var qualifiedName = parentScope.qualifiedName + "." + "<let expr>";
|
||||
return doEnter(new LetExpressionScope(currentScope, binding, slot, qualifiedName), nodeFactory);
|
||||
return doEnter(new LetExpressionScope(currentScope, binding, qualifiedName), nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterProperty(
|
||||
@@ -165,16 +174,16 @@ public final class SymbolTable {
|
||||
nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterEntry(
|
||||
public <T> T enterEntryOrElement(
|
||||
@Nullable ExpressionNode keyNode, // null for listing elements
|
||||
Function<EntryScope, T> nodeFactory) {
|
||||
Function<EntryOrElementScope, T> nodeFactory) {
|
||||
|
||||
var qualifiedName = currentScope.getQualifiedName() + currentScope.getNextEntryName(keyNode);
|
||||
var builder =
|
||||
currentScope instanceof ForGeneratorScope forScope
|
||||
? forScope.frameDescriptorBuilder
|
||||
: new FrameDescriptorBuilder();
|
||||
return doEnter(new EntryScope(currentScope, qualifiedName, builder), nodeFactory);
|
||||
return doEnter(new EntryOrElementScope(currentScope, qualifiedName, builder), nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterCustomThisScope(Function<CustomThisScope, T> nodeFactory) {
|
||||
@@ -193,9 +202,10 @@ public final class SymbolTable {
|
||||
nodeFactory);
|
||||
}
|
||||
|
||||
public <T> T enterObjectScope(ObjectBody body, Function<ObjectScope, T> nodeFactory) {
|
||||
public <T> T enterObjectScope(
|
||||
FrameSlotVariable[] bindings, Function<ObjectScope, T> nodeFactory) {
|
||||
return doEnter(
|
||||
new ObjectScope(currentScope, body, currentScope.frameDescriptorBuilder), nodeFactory);
|
||||
new ObjectScope(currentScope, bindings, currentScope.frameDescriptorBuilder), nodeFactory);
|
||||
}
|
||||
|
||||
private <T, S extends Scope> T doEnter(S scope, Function<S, T> nodeFactory) {
|
||||
@@ -224,17 +234,35 @@ public final class SymbolTable {
|
||||
protected final FrameDescriptorBuilder frameDescriptorBuilder;
|
||||
private final ConstLevel constLevel;
|
||||
protected boolean isBaseModule;
|
||||
// all for-generator slots in this scope (excludes args and let bindings).
|
||||
protected final int[] forGeneratorSlots;
|
||||
// all parameter slots in this scope; includes let bindings, function params, method params,
|
||||
// but excludes object body params (they are written one level higher)
|
||||
protected final int[] parameterSlots;
|
||||
// The properties defined on this (lexical) scope
|
||||
protected final Map<String, Member> properties = new HashMap<>();
|
||||
// The methods defined on this (lexical) scope
|
||||
protected final Map<String, Member> methods = new HashMap<>();
|
||||
|
||||
static int[] getSlots(FrameSlotVariable[] bindings) {
|
||||
if (bindings.length == 0) {
|
||||
return EMPTY_INT_ARRAY;
|
||||
}
|
||||
var ret = new int[bindings.length];
|
||||
for (var i = 0; i < ret.length; i++) {
|
||||
ret[i] = bindings[i].slot();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
private Scope(
|
||||
@Nullable Scope parent,
|
||||
@Nullable Identifier name,
|
||||
String qualifiedName,
|
||||
ConstLevel constLevel,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
int[] forGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
this.parent = parent;
|
||||
this.name = name;
|
||||
this.qualifiedName = qualifiedName;
|
||||
@@ -247,6 +275,8 @@ public final class SymbolTable {
|
||||
parent != null && parent.constLevel.biggerOrEquals(constLevel)
|
||||
? parent.constLevel
|
||||
: constLevel;
|
||||
this.forGeneratorSlots = forGeneratorSlots;
|
||||
this.parameterSlots = parameterSlots;
|
||||
}
|
||||
|
||||
public final @Nullable Scope getParent() {
|
||||
@@ -266,16 +296,26 @@ public final class SymbolTable {
|
||||
return qualifiedName;
|
||||
}
|
||||
|
||||
public FrameDescriptor buildFrameDescriptor() {
|
||||
return frameDescriptorBuilder.build();
|
||||
/**
|
||||
* Returns the frame slots inhabited by for-generator variables in this scope.
|
||||
*
|
||||
* <p>Includes outer for-generator variables.
|
||||
*/
|
||||
public int[] getForGeneratorSlots() {
|
||||
return forGeneratorSlots;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new descriptor builder that contains the same slots as the current scope's frame
|
||||
* descriptor.
|
||||
* Returns the parameter slots in this scope.
|
||||
*
|
||||
* <p>Includes let bindings, object body params, method params, lambda params
|
||||
*/
|
||||
public FrameDescriptorBuilder newFrameDescriptorBuilder() {
|
||||
return new FrameDescriptorBuilder(buildFrameDescriptor());
|
||||
public int[] getParameterSlots() {
|
||||
return parameterSlots;
|
||||
}
|
||||
|
||||
public FrameDescriptor buildFrameDescriptor() {
|
||||
return frameDescriptorBuilder.build();
|
||||
}
|
||||
|
||||
public @Nullable TypeParameter getTypeParameter(String name) {
|
||||
@@ -389,6 +429,13 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public final boolean isCustomThisScope() {
|
||||
if (this instanceof LetExpressionScope) {
|
||||
var myParent = parent;
|
||||
while (myParent instanceof LetExpressionScope) {
|
||||
myParent = myParent.getParent();
|
||||
}
|
||||
return myParent instanceof CustomThisScope;
|
||||
}
|
||||
return this instanceof CustomThisScope;
|
||||
}
|
||||
|
||||
@@ -505,21 +552,45 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public static class ObjectScope extends Scope implements LexicalScope {
|
||||
private final Map<String, Integer> params;
|
||||
private final FrameSlotVariable[] bindings;
|
||||
|
||||
/**
|
||||
* NOTE: object body params desugar to wrapping this object with a lambda call.
|
||||
*
|
||||
* <p>So, the object itself does not contribute to parameter slots in the object's frame
|
||||
* descriptor.
|
||||
*
|
||||
* <p>This code:
|
||||
*
|
||||
* <pre>{@code
|
||||
* foo { param ->
|
||||
* res = param
|
||||
* }
|
||||
* }</pre>
|
||||
*
|
||||
* Is sugar for:
|
||||
*
|
||||
* <pre>{@code
|
||||
* foo = (param) -> (super.foo.apply(param)) {
|
||||
* res = param
|
||||
* }
|
||||
* }</pre>
|
||||
*/
|
||||
private ObjectScope(
|
||||
Scope parent, ObjectBody body, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
Scope parent, FrameSlotVariable[] bindings, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(
|
||||
parent,
|
||||
parent.getNameOrNull(),
|
||||
parent.getQualifiedName(),
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder);
|
||||
params = collectParams(body);
|
||||
frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
EMPTY_INT_ARRAY);
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
public boolean hasParams() {
|
||||
return !params.isEmpty();
|
||||
return bindings.length > 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -532,10 +603,11 @@ public final class SymbolTable {
|
||||
if (prop != null) {
|
||||
return new VariableResolution.LexicalProperty(false, prop.modifiers, levelsUp);
|
||||
}
|
||||
var paramIndex = params.get(name);
|
||||
if (paramIndex != null) {
|
||||
// params are on a higher level than the properties
|
||||
return new VariableResolution.Parameter(paramIndex, levelsUp + 1);
|
||||
for (var binding : bindings) {
|
||||
if (binding.name().equals(name)) {
|
||||
// params are on a higher level than the properties
|
||||
return new VariableResolution.Parameter(binding.slot(), levelsUp + 1);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -548,19 +620,6 @@ public final class SymbolTable {
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Map<String, Integer> collectParams(ObjectBody body) {
|
||||
var params = new HashMap<String, Integer>();
|
||||
for (var i = 0; i < body.getParameters().size(); i++) {
|
||||
var param = body.getParameters().get(i);
|
||||
if (param instanceof org.pkl.parser.syntax.Parameter.TypedIdentifier ti) {
|
||||
params.put(ti.getIdentifier().getValue(), i);
|
||||
} else {
|
||||
params.put("_", i);
|
||||
}
|
||||
}
|
||||
return params;
|
||||
}
|
||||
}
|
||||
|
||||
public abstract static class TypeParameterizableScope extends Scope {
|
||||
@@ -572,8 +631,17 @@ public final class SymbolTable {
|
||||
String qualifiedName,
|
||||
ConstLevel constLevel,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
super(parent, name, qualifiedName, constLevel, frameDescriptorBuilder);
|
||||
List<TypeParameter> typeParameters,
|
||||
int[] forGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
qualifiedName,
|
||||
constLevel,
|
||||
frameDescriptorBuilder,
|
||||
forGeneratorSlots,
|
||||
parameterSlots);
|
||||
this.typeParameters = typeParameters;
|
||||
}
|
||||
|
||||
@@ -593,7 +661,14 @@ public final class SymbolTable {
|
||||
private final boolean isAmend;
|
||||
|
||||
public ModuleScope(ModuleInfo moduleInfo, boolean isBaseModule) {
|
||||
super(null, null, moduleInfo.getModuleName(), ConstLevel.NONE, new FrameDescriptorBuilder());
|
||||
super(
|
||||
null,
|
||||
null,
|
||||
moduleInfo.getModuleName(),
|
||||
ConstLevel.NONE,
|
||||
new FrameDescriptorBuilder(),
|
||||
EMPTY_INT_ARRAY,
|
||||
EMPTY_INT_ARRAY);
|
||||
this.isBaseModule = isBaseModule;
|
||||
this.moduleInfo = moduleInfo;
|
||||
this.isAmend = moduleInfo.isAmend();
|
||||
@@ -622,17 +697,25 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public static final class MethodScope extends TypeParameterizableScope implements LexicalScope {
|
||||
private final List<String> bindings;
|
||||
private final FrameSlotVariable[] bindings;
|
||||
|
||||
public MethodScope(
|
||||
MethodScope(
|
||||
Scope parent,
|
||||
Identifier name,
|
||||
String qualifiedName,
|
||||
ConstLevel constLevel,
|
||||
List<String> bindings,
|
||||
FrameSlotVariable[] bindings,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
super(parent, name, qualifiedName, constLevel, frameDescriptorBuilder, typeParameters);
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
qualifiedName,
|
||||
constLevel,
|
||||
frameDescriptorBuilder,
|
||||
typeParameters,
|
||||
EMPTY_INT_ARRAY,
|
||||
getSlots(bindings));
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
@@ -648,14 +731,21 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public static final class LambdaScope extends Scope implements LexicalScope {
|
||||
private final List<String> bindings;
|
||||
private final FrameSlotVariable[] bindings;
|
||||
|
||||
public LambdaScope(
|
||||
Scope parent,
|
||||
List<String> bindings,
|
||||
FrameSlotVariable[] bindings,
|
||||
String qualifiedName,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(parent, null, qualifiedName, parent.getConstLevel(), frameDescriptorBuilder);
|
||||
super(
|
||||
parent,
|
||||
null,
|
||||
qualifiedName,
|
||||
parent.getConstLevel(),
|
||||
frameDescriptorBuilder,
|
||||
EMPTY_INT_ARRAY,
|
||||
getSlots(bindings));
|
||||
this.bindings = bindings;
|
||||
}
|
||||
|
||||
@@ -671,8 +761,8 @@ public final class SymbolTable {
|
||||
}
|
||||
|
||||
public static final class LetExpressionScope extends Scope implements LexicalScope {
|
||||
private final @Nullable String binding;
|
||||
private final int slot;
|
||||
public static final Object LET_BINDING_SLOT = new Object();
|
||||
private final @Nullable FrameSlotVariable binding;
|
||||
|
||||
private static @Nullable Identifier getParentName(Scope parent) {
|
||||
while (parent != null && parent.name == null) {
|
||||
@@ -681,25 +771,34 @@ public final class SymbolTable {
|
||||
return parent == null ? null : parent.name;
|
||||
}
|
||||
|
||||
private static int[] getMyParameterSlots(Scope parent, @Nullable FrameSlotVariable binding) {
|
||||
var parentSlots = parent.parameterSlots;
|
||||
if (binding == null) {
|
||||
return parentSlots;
|
||||
}
|
||||
return ArrayUtils.append(parentSlots, binding.slot());
|
||||
}
|
||||
|
||||
public LetExpressionScope(
|
||||
Scope parent, @Nullable String binding, int slot, String qualifiedName) {
|
||||
Scope parent, @Nullable FrameSlotVariable binding, String qualifiedName) {
|
||||
super(
|
||||
parent,
|
||||
getParentName(parent),
|
||||
qualifiedName,
|
||||
parent.getConstLevel(),
|
||||
parent.frameDescriptorBuilder);
|
||||
parent.frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
getMyParameterSlots(parent, binding));
|
||||
this.binding = binding;
|
||||
this.slot = slot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
||||
if (name.equals("_")) {
|
||||
if (name.equals("_") || binding == null) {
|
||||
return null;
|
||||
}
|
||||
if (name.equals(binding)) {
|
||||
return new ForGeneratorOrLetVariable(slot, levelsUp);
|
||||
if (name.equals(binding.name())) {
|
||||
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -718,21 +817,87 @@ public final class SymbolTable {
|
||||
return grandParent.frameDescriptorBuilder;
|
||||
}
|
||||
|
||||
private static int[] getForGeneratorSlots(Scope parent) {
|
||||
var grandParent = parent.parent;
|
||||
assert grandParent != null;
|
||||
return grandParent.forGeneratorSlots;
|
||||
}
|
||||
|
||||
private static int[] getParameterSlots(Scope parent) {
|
||||
var grandParent = parent.parent;
|
||||
assert grandParent != null;
|
||||
return grandParent.parameterSlots;
|
||||
}
|
||||
|
||||
private EagerGeneratorScope(Scope parent, String qualifiedName) {
|
||||
super(parent, null, qualifiedName, ConstLevel.NONE, getFrameDescriptorBuilder(parent));
|
||||
super(
|
||||
parent,
|
||||
null,
|
||||
qualifiedName,
|
||||
ConstLevel.NONE,
|
||||
getFrameDescriptorBuilder(parent),
|
||||
getForGeneratorSlots(parent),
|
||||
getParameterSlots(parent));
|
||||
}
|
||||
}
|
||||
|
||||
public static final class ForGeneratorScope extends Scope implements LexicalScope {
|
||||
final List<String> params;
|
||||
private final @Nullable FrameSlotVariable keyBinding;
|
||||
private final @Nullable FrameSlotVariable valueBinding;
|
||||
|
||||
private static int[] getMyForGeneratorSlots(
|
||||
Scope parentScope,
|
||||
@Nullable FrameSlotVariable keyBinding,
|
||||
@Nullable FrameSlotVariable valueBinding) {
|
||||
var slots = parentScope.forGeneratorSlots;
|
||||
if (keyBinding != null && valueBinding != null) {
|
||||
return ArrayUtils.append(slots, keyBinding.slot(), valueBinding.slot());
|
||||
}
|
||||
if (keyBinding != null) {
|
||||
return ArrayUtils.append(slots, keyBinding.slot());
|
||||
}
|
||||
if (valueBinding != null) {
|
||||
return ArrayUtils.append(slots, valueBinding.slot());
|
||||
}
|
||||
return slots;
|
||||
}
|
||||
|
||||
// for-generators execute in the frame above their enclosing object.
|
||||
// so, the parameters of the scope outside that object is visible.
|
||||
//
|
||||
// e.g. this for-generator reads param `it` as levels up == 0
|
||||
// ```
|
||||
// (it) -> new Listing {
|
||||
// for (elem in it) {
|
||||
// doSomething(elem)
|
||||
// }
|
||||
// }
|
||||
// ```
|
||||
private static int[] getMyParameterSlots(Scope parent) {
|
||||
if (parent instanceof ObjectScope) {
|
||||
var grandParent = parent.parent;
|
||||
assert grandParent != null;
|
||||
return grandParent.parameterSlots;
|
||||
}
|
||||
return parent.parameterSlots;
|
||||
}
|
||||
|
||||
public ForGeneratorScope(
|
||||
Scope parent,
|
||||
String qualifiedName,
|
||||
List<String> params,
|
||||
@Nullable FrameSlotVariable keyBinding,
|
||||
@Nullable FrameSlotVariable valueBinding,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(parent, null, qualifiedName, ConstLevel.NONE, frameDescriptorBuilder);
|
||||
this.params = params;
|
||||
super(
|
||||
parent,
|
||||
null,
|
||||
qualifiedName,
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
getMyForGeneratorSlots(parent, keyBinding, valueBinding),
|
||||
getMyParameterSlots(parent));
|
||||
this.keyBinding = keyBinding;
|
||||
this.valueBinding = valueBinding;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -744,12 +909,11 @@ public final class SymbolTable {
|
||||
|
||||
@Override
|
||||
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
||||
if (!params.contains(name)) {
|
||||
return null;
|
||||
if (keyBinding != null && keyBinding.name().equals(name)) {
|
||||
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp);
|
||||
}
|
||||
var index = frameDescriptorBuilder.findSlot(Identifier.get(name));
|
||||
if (index >= 0) {
|
||||
return new ForGeneratorOrLetVariable(index, levelsUp);
|
||||
if (valueBinding != null && valueBinding.name().equals(name)) {
|
||||
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -767,14 +931,30 @@ public final class SymbolTable {
|
||||
String qualifiedName,
|
||||
ConstLevel constLevel,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(parent, name, qualifiedName, constLevel, frameDescriptorBuilder);
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
qualifiedName,
|
||||
constLevel,
|
||||
frameDescriptorBuilder,
|
||||
// object members inherit the for-generator slots of the parent for-generator, if it
|
||||
// exists.
|
||||
parent instanceof ForGeneratorScope ? parent.forGeneratorSlots : EMPTY_INT_ARRAY,
|
||||
parent.parameterSlots);
|
||||
}
|
||||
}
|
||||
|
||||
public static final class EntryScope extends Scope {
|
||||
public EntryScope(
|
||||
public static final class EntryOrElementScope extends Scope {
|
||||
public EntryOrElementScope(
|
||||
Scope parent, String qualifiedName, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(parent, null, qualifiedName, ConstLevel.NONE, frameDescriptorBuilder);
|
||||
super(
|
||||
parent,
|
||||
null,
|
||||
qualifiedName,
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder,
|
||||
parent instanceof ForGeneratorScope ? parent.forGeneratorSlots : EMPTY_INT_ARRAY,
|
||||
parent.parameterSlots);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -788,7 +968,15 @@ public final class SymbolTable {
|
||||
int modifiers,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
super(parent, name, qualifiedName, ConstLevel.MODULE, frameDescriptorBuilder, typeParameters);
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
qualifiedName,
|
||||
ConstLevel.MODULE,
|
||||
frameDescriptorBuilder,
|
||||
typeParameters,
|
||||
EMPTY_INT_ARRAY,
|
||||
EMPTY_INT_ARRAY);
|
||||
isClosed = VmModifier.isClosed(modifiers);
|
||||
}
|
||||
|
||||
@@ -816,7 +1004,15 @@ public final class SymbolTable {
|
||||
String qualifiedName,
|
||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||
List<TypeParameter> typeParameters) {
|
||||
super(parent, name, qualifiedName, ConstLevel.MODULE, frameDescriptorBuilder, typeParameters);
|
||||
super(
|
||||
parent,
|
||||
name,
|
||||
qualifiedName,
|
||||
ConstLevel.MODULE,
|
||||
frameDescriptorBuilder,
|
||||
typeParameters,
|
||||
parent.forGeneratorSlots,
|
||||
parent.parameterSlots);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -841,7 +1037,9 @@ public final class SymbolTable {
|
||||
parent.getNameOrNull(),
|
||||
parent.getQualifiedName(),
|
||||
ConstLevel.NONE,
|
||||
frameDescriptorBuilder);
|
||||
frameDescriptorBuilder,
|
||||
parent.forGeneratorSlots,
|
||||
parent.parameterSlots);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -849,18 +1047,22 @@ public final class SymbolTable {
|
||||
public AnnotationScope(
|
||||
Scope parent, String qualifiedName, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||
super(
|
||||
parent, parent.getNameOrNull(), qualifiedName, ConstLevel.MODULE, frameDescriptorBuilder);
|
||||
parent,
|
||||
parent.getNameOrNull(),
|
||||
qualifiedName,
|
||||
ConstLevel.MODULE,
|
||||
frameDescriptorBuilder,
|
||||
EMPTY_INT_ARRAY,
|
||||
EMPTY_INT_ARRAY);
|
||||
}
|
||||
}
|
||||
|
||||
private static @Nullable VariableResolution resolveParameter(
|
||||
String name, List<String> bindings, int levelsUp) {
|
||||
if (name.equals("_")) {
|
||||
return null;
|
||||
}
|
||||
var index = bindings.indexOf(name);
|
||||
if (index != -1) {
|
||||
return new VariableResolution.Parameter(index, levelsUp);
|
||||
String name, FrameSlotVariable[] bindings, int levelsUp) {
|
||||
for (var binding : bindings) {
|
||||
if (name.equals(binding.name())) {
|
||||
return new Parameter(binding.slot(), levelsUp);
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -34,7 +34,7 @@ public sealed interface VariableResolution {
|
||||
// method, lambda, object body param
|
||||
record Parameter(int slot, int levelsUp) implements VariableResolution {}
|
||||
|
||||
record ForGeneratorOrLetVariable(int slot, int levelsUp) implements VariableResolution {}
|
||||
record ForGeneratorVariableOrLetBinding(int slot, int levelsUp) implements VariableResolution {}
|
||||
|
||||
// Implicit base module lookup
|
||||
record ImplicitBaseProperty() implements VariableResolution {}
|
||||
|
||||
@@ -58,6 +58,7 @@ public abstract class LetExprNode extends ExpressionNode {
|
||||
typeNode = new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection());
|
||||
}
|
||||
typeNode.initWriteSlotNode(slot);
|
||||
frame.getFrameDescriptor().setSlotKind(slot, typeNode.getFrameSlotKind());
|
||||
insert(typeNode);
|
||||
}
|
||||
assert typeNode != null;
|
||||
|
||||
@@ -28,6 +28,7 @@ import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.type.TypeNode;
|
||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||
import org.pkl.core.runtime.*;
|
||||
import org.pkl.core.util.ArrayUtils;
|
||||
|
||||
public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
private final FrameDescriptor generatorDescriptor;
|
||||
@@ -37,6 +38,9 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
@Children private final GeneratorMemberNode[] childNodes;
|
||||
@Child private @Nullable TypeNode keyTypeNode;
|
||||
@Child private @Nullable TypeNode valueTypeNode;
|
||||
private final int keySlot;
|
||||
private final int valueSlot;
|
||||
private final int[] slotsToCopy;
|
||||
|
||||
public GeneratorForNode(
|
||||
SourceSection sourceSection,
|
||||
@@ -52,7 +56,11 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
@Nullable TypeNode keyTypeNode,
|
||||
// If this node can be constructed at parse time,
|
||||
// it should be passed instead of `unresolvedValueTypeNode`.
|
||||
@Nullable TypeNode valueTypeNode) {
|
||||
@Nullable TypeNode valueTypeNode,
|
||||
int keySlot,
|
||||
int valueSlot,
|
||||
int[] outerForGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
super(sourceSection, false);
|
||||
this.generatorDescriptor = generatorDescriptor;
|
||||
this.iterableNode = iterableNode;
|
||||
@@ -61,6 +69,9 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
this.childNodes = childNodes;
|
||||
this.keyTypeNode = keyTypeNode;
|
||||
this.valueTypeNode = valueTypeNode;
|
||||
this.keySlot = keySlot;
|
||||
this.valueSlot = valueSlot;
|
||||
this.slotsToCopy = ArrayUtils.concat(parameterSlots, outerForGeneratorSlots);
|
||||
}
|
||||
|
||||
protected abstract void executeWithIterable(
|
||||
@@ -154,12 +165,10 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
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
|
||||
// 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());
|
||||
VmUtils.copyLocals(frame, newFrame, slotsToCopy);
|
||||
if (keyTypeNode != null) {
|
||||
keyTypeNode.executeAndSet(newFrame, key);
|
||||
}
|
||||
@@ -175,14 +184,12 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||
private void initialize(VirtualFrame frame) {
|
||||
if (unresolvedKeyTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var keySlot = frame.getFrameDescriptor().getNumberOfSlots();
|
||||
keyTypeNode = insert(unresolvedKeyTypeNode.execute(frame)).initWriteSlotNode(keySlot);
|
||||
generatorDescriptor.setSlotKind(keySlot, keyTypeNode.getFrameSlotKind());
|
||||
unresolvedKeyTypeNode = null;
|
||||
}
|
||||
if (unresolvedValueTypeNode != null) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
var valueSlot = frame.getFrameDescriptor().getNumberOfSlots() + (keyTypeNode != null ? 1 : 0);
|
||||
valueTypeNode = insert(unresolvedValueTypeNode.execute(frame)).initWriteSlotNode(valueSlot);
|
||||
generatorDescriptor.setSlotKind(valueSlot, valueTypeNode.getFrameSlotKind());
|
||||
unresolvedValueTypeNode = null;
|
||||
|
||||
+6
-7
@@ -18,27 +18,26 @@ 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;
|
||||
import org.pkl.core.util.ArrayUtils;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
private final int[] slotsToCopy;
|
||||
|
||||
public RestoreForBindingsNode(ExpressionNode child) {
|
||||
public RestoreForBindingsNode(
|
||||
ExpressionNode child, int[] parameterSlots, int[] forGeneratorSlots) {
|
||||
super(child.getSourceSection());
|
||||
this.child = child;
|
||||
this.slotsToCopy = ArrayUtils.concat(parameterSlots, forGeneratorSlots);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var generatorFrame = ObjectData.getGeneratorFrame(frame);
|
||||
// copying all slots includes function arguments, but the capture generator frame
|
||||
// and the host frame are guaranteed to have the same arguments and number of slots
|
||||
// (guaranteed by AstBuilder).
|
||||
assert frame.getFrameDescriptor().getNumberOfSlots()
|
||||
== generatorFrame.getFrameDescriptor().getNumberOfSlots();
|
||||
VmUtils.copyLocals(generatorFrame, 0, frame, 0, frame.getFrameDescriptor().getNumberOfSlots());
|
||||
VmUtils.copyLocals(generatorFrame, frame, slotsToCopy);
|
||||
return child.executeGeneric(frame);
|
||||
}
|
||||
}
|
||||
|
||||
+68
@@ -0,0 +1,68 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.primary;
|
||||
|
||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import com.oracle.truffle.api.nodes.DirectCallNode;
|
||||
import com.oracle.truffle.api.source.SourceSection;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.ast.SimpleRootNode;
|
||||
import org.pkl.core.ast.builder.SymbolTable.CustomThisScope;
|
||||
import org.pkl.core.runtime.VmLanguage;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
|
||||
/**
|
||||
* A node that executes {@code expressionNode} within its own root node, with the custom this value
|
||||
* set.
|
||||
*
|
||||
* <p>This is required, for example, when let expressions are used inside type constraints, because:
|
||||
*
|
||||
* <ul>
|
||||
* <li>let expressions write to frame slots.
|
||||
* <li>type nodes don't add slots to the enclosing frame.
|
||||
* </ul>
|
||||
*/
|
||||
public final class ExecuteCustomThisWithRootNode extends ExpressionNode {
|
||||
private @Child ExpressionNode customThisNode =
|
||||
new CustomThisNode(VmUtils.unavailableSourceSection());
|
||||
private @Child DirectCallNode callNode;
|
||||
|
||||
public ExecuteCustomThisWithRootNode(
|
||||
SourceSection sourceSection,
|
||||
ExpressionNode expressionNode,
|
||||
FrameDescriptor frameDescriptor,
|
||||
String qualifiedName,
|
||||
int[] forGeneratorSlots,
|
||||
int[] parameterSlots) {
|
||||
super(sourceSection);
|
||||
frameDescriptor.findOrAddAuxiliarySlot(CustomThisScope.FRAME_SLOT_ID);
|
||||
var rootNode =
|
||||
new SimpleRootNode(
|
||||
VmLanguage.get(this),
|
||||
frameDescriptor,
|
||||
sourceSection,
|
||||
qualifiedName,
|
||||
new WithCustomThisExpression(expressionNode, forGeneratorSlots, parameterSlots));
|
||||
this.callNode = DirectCallNode.create(rootNode.getCallTarget());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var customThis = customThisNode.executeGeneric(frame);
|
||||
return callNode.call(VmUtils.getReceiver(frame), VmUtils.getOwner(frame), customThis, frame);
|
||||
}
|
||||
}
|
||||
@@ -32,8 +32,8 @@ public final class GetModuleNode extends ExpressionNode {
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
|
||||
var levelsUp = 0;
|
||||
for (var current = VmUtils.getOwner(frame).getEnclosingOwner();
|
||||
var levelsUp = -1;
|
||||
for (var current = VmUtils.getOwner(frame);
|
||||
current != null;
|
||||
current = current.getEnclosingOwner()) {
|
||||
if (!current.isParseTimeInvisibleScope()) {
|
||||
|
||||
+56
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.ast.expression.primary;
|
||||
|
||||
import com.oracle.truffle.api.CompilerDirectives;
|
||||
import com.oracle.truffle.api.CompilerDirectives.CompilationFinal;
|
||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||
import org.pkl.core.ast.ExpressionNode;
|
||||
import org.pkl.core.runtime.VmUtils;
|
||||
import org.pkl.core.util.ArrayUtils;
|
||||
|
||||
/** Entrypoint node of {@link ExecuteCustomThisWithRootNode}. */
|
||||
public final class WithCustomThisExpression extends ExpressionNode {
|
||||
|
||||
private @Child ExpressionNode expressionNode;
|
||||
private final int[] slotsToCopy;
|
||||
@CompilationFinal private int customThisSlot = -1;
|
||||
|
||||
public WithCustomThisExpression(
|
||||
ExpressionNode expressionNode, int[] forGeneratorSlots, int[] parameterSlots) {
|
||||
super(expressionNode.getSourceSection());
|
||||
this.expressionNode = expressionNode;
|
||||
this.slotsToCopy = ArrayUtils.concat(parameterSlots, forGeneratorSlots);
|
||||
}
|
||||
|
||||
public int getCustomThisSlot(VirtualFrame frame) {
|
||||
if (customThisSlot == -1) {
|
||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||
customThisSlot = VmUtils.findCustomThisSlot(frame);
|
||||
}
|
||||
return customThisSlot;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object executeGeneric(VirtualFrame frame) {
|
||||
var customThisSlot = getCustomThisSlot(frame);
|
||||
// arguments passed in by `ExecuteCustomThisWithRootNode`
|
||||
frame.setAuxiliarySlot(customThisSlot, frame.getArguments()[2]);
|
||||
var originalFrame = (VirtualFrame) frame.getArguments()[3];
|
||||
VmUtils.copyLocals(originalFrame, frame, slotsToCopy);
|
||||
return expressionNode.executeGeneric(frame);
|
||||
}
|
||||
}
|
||||
@@ -148,8 +148,7 @@ public final class FunctionNode extends RegularMemberNode {
|
||||
var parameters = CollectionUtils.<String, PType>newLinkedHashMap(paramCount);
|
||||
for (var i = 0; i < paramCount; i++) {
|
||||
var slotName = getFrameDescriptor().getSlotName(i);
|
||||
// Ignored parameters (`_`) have no name
|
||||
var paramName = slotName == null ? "_#" + i : slotName.toString();
|
||||
var paramName = slotName == Identifier.ILLEGAL ? "_#" + i : slotName.toString();
|
||||
parameters.put(paramName, TypeNode.export(parameterTypeNodes[i]));
|
||||
}
|
||||
|
||||
|
||||
@@ -20,10 +20,7 @@ import com.oracle.truffle.api.frame.FrameSlotKind;
|
||||
import java.util.Arrays;
|
||||
import org.jspecify.annotations.Nullable;
|
||||
|
||||
/**
|
||||
* A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also lets us find the slot of a
|
||||
* given {@link Identifier}.
|
||||
*/
|
||||
/** A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also gives us the current size. */
|
||||
public class FrameDescriptorBuilder {
|
||||
|
||||
private @Nullable Identifier[] names;
|
||||
@@ -42,16 +39,6 @@ public class FrameDescriptorBuilder {
|
||||
this.names = new Identifier[capacity];
|
||||
}
|
||||
|
||||
public FrameDescriptorBuilder(FrameDescriptor descriptor) {
|
||||
this(descriptor.getNumberOfSlots());
|
||||
for (var i = 0; i < descriptor.getNumberOfSlots(); i++) {
|
||||
addSlot(
|
||||
descriptor.getSlotKind(i),
|
||||
(Identifier) descriptor.getSlotName(i),
|
||||
descriptor.getSlotInfo(i));
|
||||
}
|
||||
}
|
||||
|
||||
private void ensureCapacity(int count) {
|
||||
if (names.length < size + count) {
|
||||
var newLength = Math.max(size + count, size * 2);
|
||||
@@ -59,26 +46,19 @@ public class FrameDescriptorBuilder {
|
||||
}
|
||||
}
|
||||
|
||||
public int addSlot(FrameSlotKind kind, @Nullable Identifier name, @Nullable Object info) {
|
||||
public FrameSlotVariable addSlot(FrameSlotKind kind, Identifier name, @Nullable Object info) {
|
||||
ensureCapacity(1);
|
||||
names[size] = name;
|
||||
size++;
|
||||
return underlying.addSlot(kind, name, info);
|
||||
}
|
||||
|
||||
public int findSlot(Identifier identifier) {
|
||||
// go backwards to account for shadowed variables
|
||||
// (this happens in the case of nested for generators).
|
||||
for (var i = size - 1; i >= 0; i--) {
|
||||
var name = names[i];
|
||||
if (name != null && name.equals(identifier)) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
var slot = underlying.addSlot(kind, name, info);
|
||||
return new FrameSlotVariable(name.toString(), slot);
|
||||
}
|
||||
|
||||
public FrameDescriptor build() {
|
||||
return underlying.build();
|
||||
}
|
||||
|
||||
public int getSize() {
|
||||
return size;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,18 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.runtime;
|
||||
|
||||
public record FrameSlotVariable(String name, int slot) {}
|
||||
@@ -163,6 +163,8 @@ public final class Identifier implements Comparable<Identifier> {
|
||||
public static final Identifier GLOB = get("glob");
|
||||
public static final Identifier COMPLETION_CANDIDATES = get("completionCandidates");
|
||||
|
||||
public static final Identifier ILLEGAL = get("`");
|
||||
|
||||
// common in lambdas etc
|
||||
public static final Identifier IT = get("it");
|
||||
|
||||
|
||||
@@ -401,50 +401,42 @@ public final class VmUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* Copies {@code numberOfLocalsToCopy} locals from {@code sourceFrame}, starting at {@code
|
||||
* firstSourceSlot}, to {@code firstSourceSlot}, starting at {@code firstTargetSlot}.
|
||||
* Copies the slots specified by {@code slotsToCopy} locals from {@code sourceFrame} into {@code
|
||||
* targetFrame}.
|
||||
*/
|
||||
public static void copyLocals(
|
||||
VirtualFrame sourceFrame,
|
||||
int firstSourceSlot,
|
||||
VirtualFrame targetFrame,
|
||||
int firstTargetSlot,
|
||||
int numberOfLocalsToCopy) {
|
||||
VirtualFrame sourceFrame, VirtualFrame targetFrame, int[] slotsToCopy) {
|
||||
if (slotsToCopy.length == 0) return;
|
||||
var sourceDescriptor = sourceFrame.getFrameDescriptor();
|
||||
var targetDescriptor = targetFrame.getFrameDescriptor();
|
||||
assert sourceDescriptor.getNumberOfSlots() <= targetDescriptor.getNumberOfSlots();
|
||||
// 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;
|
||||
for (var slot : slotsToCopy) {
|
||||
// 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);
|
||||
var slotKind = sourceDescriptor.getSlotKind(slot);
|
||||
switch (slotKind) {
|
||||
case Boolean -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Boolean);
|
||||
targetFrame.setBoolean(targetSlot, sourceFrame.getBoolean(sourceSlot));
|
||||
targetDescriptor.setSlotKind(slot, FrameSlotKind.Boolean);
|
||||
targetFrame.setBoolean(slot, sourceFrame.getBoolean(slot));
|
||||
}
|
||||
case Long -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Long);
|
||||
targetFrame.setLong(targetSlot, sourceFrame.getLong(sourceSlot));
|
||||
targetDescriptor.setSlotKind(slot, FrameSlotKind.Long);
|
||||
targetFrame.setLong(slot, sourceFrame.getLong(slot));
|
||||
}
|
||||
case Double -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Double);
|
||||
targetFrame.setDouble(targetSlot, sourceFrame.getDouble(sourceSlot));
|
||||
targetDescriptor.setSlotKind(slot, FrameSlotKind.Double);
|
||||
targetFrame.setDouble(slot, sourceFrame.getDouble(slot));
|
||||
}
|
||||
case Object -> {
|
||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Object);
|
||||
targetDescriptor.setSlotKind(slot, FrameSlotKind.Object);
|
||||
targetFrame.setObject(
|
||||
targetSlot,
|
||||
slot,
|
||||
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));
|
||||
// Hence, we need to read the local with getValue() instead of getObject().
|
||||
? sourceFrame.getValue(slot)
|
||||
: sourceFrame.getObject(slot));
|
||||
}
|
||||
default -> {
|
||||
CompilerDirectives.transferToInterpreter();
|
||||
@@ -978,10 +970,9 @@ public final class VmUtils {
|
||||
}
|
||||
|
||||
public static int findCustomThisSlot(VirtualFrame frame) {
|
||||
return frame
|
||||
.getFrameDescriptor()
|
||||
.getAuxiliarySlots()
|
||||
.getOrDefault(CustomThisScope.FRAME_SLOT_ID, -1);
|
||||
var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CustomThisScope.FRAME_SLOT_ID);
|
||||
assert result != null;
|
||||
return result;
|
||||
}
|
||||
|
||||
@TruffleBoundary
|
||||
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright © 2026 Apple Inc. and the Pkl project authors. All rights reserved.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* https://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package org.pkl.core.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
public final class ArrayUtils {
|
||||
private ArrayUtils() {}
|
||||
|
||||
public static final int[] EMPTY_INT_ARRAY = new int[0];
|
||||
|
||||
public static int[] append(int[] array, int elem) {
|
||||
if (array.length == 0) {
|
||||
return new int[] {elem};
|
||||
}
|
||||
var ret = Arrays.copyOf(array, array.length + 1);
|
||||
ret[array.length] = elem;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int[] append(int[] array, int elem1, int elem2) {
|
||||
if (array.length == 0) {
|
||||
return new int[] {elem1, elem2};
|
||||
}
|
||||
var ret = Arrays.copyOf(array, array.length + 2);
|
||||
ret[array.length] = elem1;
|
||||
ret[array.length + 1] = elem2;
|
||||
return ret;
|
||||
}
|
||||
|
||||
public static int[] concat(int[] array1, int[] array2) {
|
||||
var result = new int[array1.length + array2.length];
|
||||
System.arraycopy(array1, 0, result, 0, array1.length);
|
||||
System.arraycopy(array2, 0, result, array1.length, array2.length);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
// let expr inside custom this scopes
|
||||
typealias Foo = String(let (x = this) this == x)
|
||||
|
||||
typealias Foo2 = String(let (x = "foo") this.startsWith(x), let (y = "o") this.endsWith(y))
|
||||
|
||||
typealias Foo3 = String(let (x = "foo") this.startsWith(x))(let (y = "o") this.endsWith(y))
|
||||
|
||||
res1: Foo = "foo"
|
||||
|
||||
res2: Foo2 = "foo"
|
||||
|
||||
res3: Foo3 = "foo"
|
||||
|
||||
hidden bar {
|
||||
new {
|
||||
name = "fooey"
|
||||
}
|
||||
}
|
||||
|
||||
res4 = (bar) {
|
||||
[[let (x = "foo") this.name.startsWith(x)]] {
|
||||
name = "bob"
|
||||
}
|
||||
}
|
||||
|
||||
res5 = (bar) {
|
||||
[[let (x = this) x.name.startsWith("foo")]] {
|
||||
name = "bob"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
res =
|
||||
let (
|
||||
x = new Listing {
|
||||
for (elem in List(1, 2)) {
|
||||
elem + 5
|
||||
}
|
||||
}
|
||||
)
|
||||
x
|
||||
|
||||
res2 =
|
||||
let (qux = 1)
|
||||
let (
|
||||
x = new Listing {
|
||||
for (num in List(1, 2)) {
|
||||
qux + num
|
||||
}
|
||||
}
|
||||
)
|
||||
x
|
||||
|
||||
res3 =
|
||||
let (qux = 1)
|
||||
let (
|
||||
x = new Listing {
|
||||
for (num in List(1, 2)) {
|
||||
qux + num
|
||||
}
|
||||
}
|
||||
)
|
||||
let (y = 5)
|
||||
let (
|
||||
z = new Listing {
|
||||
for (nummm in List(1, 2)) {
|
||||
when (qux + y == nummm) {
|
||||
nummm + y
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
new { ...x; ...z }
|
||||
|
||||
res4 =
|
||||
let (m: Mapping<String, String> = new Mapping {
|
||||
["foo"] = "bar"
|
||||
})
|
||||
new Mapping {
|
||||
for (k, _ in Map("foo", "bar")) {
|
||||
[k] {
|
||||
m[k]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
prop = "qux"
|
||||
|
||||
res =
|
||||
new Mapping {
|
||||
default {
|
||||
when (module.prop == "qux") {
|
||||
myProp = "qux"
|
||||
}
|
||||
}
|
||||
["bar"] {}
|
||||
}
|
||||
+13
@@ -480,3 +480,16 @@ whenWithElse = new Dynamic {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
local func = (it) -> new Listing {
|
||||
for (elem1 in List(1, 2)) {
|
||||
it
|
||||
new Listing {
|
||||
for (elem2 in List(1, 2)) {
|
||||
elem1 + elem2
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
nestedForsWithinLambda = func.apply("hi")
|
||||
|
||||
+39
@@ -475,3 +475,42 @@ withinLetExpr =
|
||||
[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
// object bodies that are children of for-generators inherit the frame descriptor of the parent object
|
||||
nestedObjectInsideFor {
|
||||
for (idx, qux in List(1, 2)) {
|
||||
[idx] {
|
||||
for (baz in List(1, 2)) {
|
||||
when (baz == qux) {
|
||||
baz
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// object bodies inherit the frame descriptor of the parent object
|
||||
doublyNestedObjectInsideFor {
|
||||
for (idx, qux in List(1, 2)) {
|
||||
[idx] {
|
||||
new Listing {
|
||||
for (baz in List(1, 2)) {
|
||||
when (baz == qux) {
|
||||
baz
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insideObjectBodyWithParam: Mapping<String, Dynamic> = new {
|
||||
default { key ->
|
||||
for (key, value in Map("foo", List(new Dynamic { bar = 1 }))) {
|
||||
[key] {
|
||||
...value
|
||||
}
|
||||
}
|
||||
}
|
||||
["res"] {}
|
||||
}
|
||||
|
||||
Vendored
+1
-1
@@ -1,7 +1,7 @@
|
||||
examples {
|
||||
local a = List("1", "2", "3", "4")
|
||||
local b = List("a", "b", "c", "d")
|
||||
|
||||
|
||||
["shadow key variable"] {
|
||||
new {
|
||||
for (key, outerValue in a) {
|
||||
|
||||
Vendored
+5
@@ -0,0 +1,5 @@
|
||||
res {
|
||||
for (prefix in List("foo")) {
|
||||
"foobar" as String(let (y = this) y.startsWith(prefix))
|
||||
}
|
||||
}
|
||||
+27
-1
@@ -43,7 +43,8 @@ res5 = new {
|
||||
|
||||
// nested predicate
|
||||
res6 = (people) {
|
||||
[[(people) { [[name == "Barn Owl"]] { age = 99 } }.toList().find((it) -> it.age == 99).name == name]] {
|
||||
[[(people) { [[name == "Barn Owl"]] { age = 99 } }.toList().find((it) -> it.age == 99).name
|
||||
== name]] {
|
||||
age = 55
|
||||
}
|
||||
}
|
||||
@@ -86,3 +87,28 @@ res12 = (people) {
|
||||
[[name == "Pigeon"]] { age = 122 }
|
||||
}
|
||||
}
|
||||
|
||||
res13 = (people) {
|
||||
[[(() -> this.name == "Pigeon").apply()]] {
|
||||
age = 99
|
||||
}
|
||||
}
|
||||
|
||||
res14 = (people) {
|
||||
for (foo in List(1, 2)) {
|
||||
[[foo == 1]] {
|
||||
age = foo
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res15 =
|
||||
(
|
||||
(it) -> (people) {
|
||||
for (foo in List(1, 2)) {
|
||||
[[foo == 1]] {
|
||||
age = foo
|
||||
}
|
||||
}
|
||||
}
|
||||
).apply("hi")
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
res1 = "foo"
|
||||
res2 = "foo"
|
||||
res3 = "foo"
|
||||
res4 {
|
||||
new {
|
||||
name = "bob"
|
||||
}
|
||||
}
|
||||
res5 {
|
||||
new {
|
||||
name = "bob"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
res {
|
||||
6
|
||||
7
|
||||
}
|
||||
res2 {
|
||||
2
|
||||
3
|
||||
}
|
||||
res3 {
|
||||
2
|
||||
3
|
||||
}
|
||||
res4 {
|
||||
["foo"] {
|
||||
"bar"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
prop = "qux"
|
||||
res {
|
||||
["bar"] {
|
||||
myProp = "qux"
|
||||
}
|
||||
}
|
||||
+12
@@ -335,3 +335,15 @@ whenWithElse {
|
||||
20
|
||||
40
|
||||
}
|
||||
nestedForsWithinLambda {
|
||||
"hi"
|
||||
new {
|
||||
2
|
||||
3
|
||||
}
|
||||
"hi"
|
||||
new {
|
||||
3
|
||||
4
|
||||
}
|
||||
}
|
||||
|
||||
+29
@@ -323,3 +323,32 @@ withinLetExpr {
|
||||
["a"] = 1
|
||||
["b"] = 2
|
||||
}
|
||||
nestedObjectInsideFor {
|
||||
[0] {
|
||||
1
|
||||
}
|
||||
[1] {
|
||||
2
|
||||
}
|
||||
}
|
||||
doublyNestedObjectInsideFor {
|
||||
[0] {
|
||||
new {
|
||||
1
|
||||
}
|
||||
}
|
||||
[1] {
|
||||
new {
|
||||
2
|
||||
}
|
||||
}
|
||||
}
|
||||
insideObjectBodyWithParam {
|
||||
["res"] {
|
||||
["foo"] {
|
||||
new {
|
||||
bar = 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vendored
+3
@@ -0,0 +1,3 @@
|
||||
res {
|
||||
"foobar"
|
||||
}
|
||||
+42
@@ -153,3 +153,45 @@ res12 {
|
||||
age = 33
|
||||
}
|
||||
}
|
||||
res13 {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 99
|
||||
}
|
||||
new {
|
||||
name = "Barn Owl"
|
||||
age = 21
|
||||
}
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 33
|
||||
}
|
||||
}
|
||||
res14 {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 1
|
||||
}
|
||||
new {
|
||||
name = "Barn Owl"
|
||||
age = 1
|
||||
}
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 1
|
||||
}
|
||||
}
|
||||
res15 {
|
||||
new {
|
||||
name = "Pigeon"
|
||||
age = 1
|
||||
}
|
||||
new {
|
||||
name = "Barn Owl"
|
||||
age = 1
|
||||
}
|
||||
new {
|
||||
name = "Parrot"
|
||||
age = 1
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user