mirror of
https://github.com/apple/pkl.git
synced 2026-06-12 00:24:37 +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.MethodResolution.LexicalMethod;
|
||||||
import org.pkl.core.ast.builder.SymbolTable.AnnotationScope;
|
import org.pkl.core.ast.builder.SymbolTable.AnnotationScope;
|
||||||
import org.pkl.core.ast.builder.SymbolTable.ClassScope;
|
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.ModuleScope;
|
||||||
import org.pkl.core.ast.builder.SymbolTable.ObjectScope;
|
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.ImplicitBaseProperty;
|
||||||
import org.pkl.core.ast.builder.VariableResolution.ImplicitThisProperty;
|
import org.pkl.core.ast.builder.VariableResolution.ImplicitThisProperty;
|
||||||
import org.pkl.core.ast.builder.VariableResolution.LexicalProperty;
|
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.ReadPropertyNodeGen;
|
||||||
import org.pkl.core.ast.expression.member.ReadSuperEntryNode;
|
import org.pkl.core.ast.expression.member.ReadSuperEntryNode;
|
||||||
import org.pkl.core.ast.expression.member.ReadSuperPropertyNode;
|
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.GetEnclosingReceiverNode;
|
||||||
import org.pkl.core.ast.expression.primary.GetMemberKeyNode;
|
import org.pkl.core.ast.expression.primary.GetMemberKeyNode;
|
||||||
import org.pkl.core.ast.expression.primary.GetModuleNode;
|
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.packages.PackageLoadError;
|
||||||
import org.pkl.core.runtime.BaseModule;
|
import org.pkl.core.runtime.BaseModule;
|
||||||
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||||
|
import org.pkl.core.runtime.FrameSlotVariable;
|
||||||
import org.pkl.core.runtime.ModuleInfo;
|
import org.pkl.core.runtime.ModuleInfo;
|
||||||
import org.pkl.core.runtime.ModuleResolver;
|
import org.pkl.core.runtime.ModuleResolver;
|
||||||
import org.pkl.core.runtime.VmBytes;
|
import org.pkl.core.runtime.VmBytes;
|
||||||
@@ -417,14 +420,33 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
scope -> {
|
scope -> {
|
||||||
var exprs = type.getExprs();
|
var exprs = type.getExprs();
|
||||||
var constraints = new TypeConstraintNode[exprs.size()];
|
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 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);
|
constraints[i] = TypeConstraintNodeGen.create(expr.getSourceSection(), expr);
|
||||||
}
|
}
|
||||||
return new Constrained(createSourceSection(type), language, childNode, constraints);
|
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
|
@Override
|
||||||
public UnresolvedTypeNode visitUnionType(UnionType type) {
|
public UnresolvedTypeNode visitUnionType(UnionType type) {
|
||||||
var elementTypes = type.getTypes();
|
var elementTypes = type.getTypes();
|
||||||
@@ -682,13 +704,13 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
MemberLookupMode.IMPLICIT_LEXICAL,
|
MemberLookupMode.IMPLICIT_LEXICAL,
|
||||||
needsConst,
|
needsConst,
|
||||||
p.levelsUp() == 0 ? new GetReceiverNode() : new GetEnclosingReceiverNode(p.levelsUp()));
|
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
|
// 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
|
// 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.
|
// always write to frame slots in the same frame.
|
||||||
//
|
//
|
||||||
// function foo(bar) = new Mixin {
|
// function foo(bar) = new Mixin {
|
||||||
// [bar] = 1 <--- actually 1 level, not 0
|
// res = bar <--- actually 1 level, not 0
|
||||||
// for (elem in qux) {
|
// for (elem in qux) {
|
||||||
// elem <--- actually 0 level
|
// elem <--- actually 0 level
|
||||||
// }
|
// }
|
||||||
@@ -1096,26 +1118,30 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
var sourceSection = createSourceSection(letExpr);
|
var sourceSection = createSourceSection(letExpr);
|
||||||
var parameter = letExpr.getParameter();
|
var parameter = letExpr.getParameter();
|
||||||
UnresolvedTypeNode typeNode = null;
|
UnresolvedTypeNode typeNode = null;
|
||||||
String binding = null;
|
@Nullable FrameSlotVariable binding = null;
|
||||||
var slot = -1;
|
|
||||||
var frameDescriptorBuilder = symbolTable.getCurrentScope().frameDescriptorBuilder;
|
var frameDescriptorBuilder = symbolTable.getCurrentScope().frameDescriptorBuilder;
|
||||||
if (parameter instanceof TypedIdentifier par) {
|
if (parameter instanceof TypedIdentifier par) {
|
||||||
typeNode = visitTypeAnnotation(par.getTypeAnnotation());
|
typeNode = visitTypeAnnotation(par.getTypeAnnotation());
|
||||||
slot =
|
binding =
|
||||||
frameDescriptorBuilder.addSlot(
|
frameDescriptorBuilder.addSlot(
|
||||||
FrameSlotKind.Illegal, toIdentifier(par.getIdentifier().getValue()), null);
|
FrameSlotKind.Illegal,
|
||||||
binding = par.getIdentifier().getValue();
|
toIdentifier(par.getIdentifier().getValue()),
|
||||||
|
LetExpressionScope.LET_BINDING_SLOT);
|
||||||
}
|
}
|
||||||
var bindingExpr = visitExpr(letExpr.getBindingExpr());
|
var bindingExpr = visitExpr(letExpr.getBindingExpr());
|
||||||
var t = typeNode;
|
var t = typeNode;
|
||||||
var s = slot;
|
var b = binding;
|
||||||
return symbolTable.enterLetExpression(
|
return symbolTable.enterLetExpression(
|
||||||
binding,
|
binding,
|
||||||
slot,
|
|
||||||
scope -> {
|
scope -> {
|
||||||
var bodyExpr = visitExpr(letExpr.getExpr());
|
var bodyExpr = visitExpr(letExpr.getExpr());
|
||||||
return LetExprNodeGen.create(
|
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) {
|
public ExpressionNode visitFunctionLiteralExpr(FunctionLiteralExpr expr) {
|
||||||
var sourceSection = createSourceSection(expr);
|
var sourceSection = createSourceSection(expr);
|
||||||
var params = expr.getParameterList();
|
var params = expr.getParameterList();
|
||||||
var descriptorBuilder = createFrameDescriptorBuilder(params);
|
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(params);
|
||||||
var paramCount = params.getParameters().size();
|
var paramCount = params.getParameters().size();
|
||||||
|
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||||
|
var bindings = descriptorBuilderAndBindings.second;
|
||||||
if (paramCount > 5) {
|
if (paramCount > 5) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("tooManyFunctionParameters")
|
.evalError("tooManyFunctionParameters")
|
||||||
@@ -1133,8 +1160,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
.build();
|
.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
var bindings = getParameterNames(params);
|
|
||||||
|
|
||||||
var isCustomThisScope = symbolTable.getCurrentScope().isCustomThisScope();
|
var isCustomThisScope = symbolTable.getCurrentScope().isCustomThisScope();
|
||||||
|
|
||||||
return symbolTable.enterLambda(
|
return symbolTable.enterLambda(
|
||||||
@@ -1281,7 +1306,18 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
public GeneratorMemberNode visitMemberPredicate(MemberPredicate ctx) {
|
public GeneratorMemberNode visitMemberPredicate(MemberPredicate ctx) {
|
||||||
var keyNode =
|
var keyNode =
|
||||||
symbolTable.enterEagerGenerator(
|
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 =
|
var member =
|
||||||
doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.getExpr(), ctx.getBodyList());
|
doVisitObjectEntryBody(createSourceSection(ctx), keyNode, ctx.getExpr(), ctx.getBodyList());
|
||||||
var isFrameStored =
|
var isFrameStored =
|
||||||
@@ -1339,82 +1375,81 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
return doVisitGeneratorMemberNodes(body.getMembers());
|
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
|
@Override
|
||||||
public GeneratorMemberNode visitForGenerator(ForGenerator ctx) {
|
public GeneratorMemberNode visitForGenerator(ForGenerator ctx) {
|
||||||
|
|
||||||
var keyParameter = ctx.getP2() == null ? null : ctx.getP1();
|
var keyParameter = ctx.getP2() == null ? null : ctx.getP1();
|
||||||
var valueParameter = ctx.getP2() == null ? ctx.getP1() : ctx.getP2();
|
var valueParameter = ctx.getP2() == null ? ctx.getP1() : ctx.getP2();
|
||||||
TypedIdentifier keyTypedIdentifier = null;
|
var keyBinding = makeBinding(keyParameter);
|
||||||
if (keyParameter instanceof TypedIdentifier ti) keyTypedIdentifier = ti;
|
var valueBinding = makeBinding(valueParameter);
|
||||||
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 keyIdentifier =
|
var keyIdentifier =
|
||||||
keyTypedIdentifier == null
|
keyParameter instanceof TypedIdentifier keyTypedIdentifier ? keyTypedIdentifier : null;
|
||||||
? null
|
|
||||||
: toIdentifier(keyTypedIdentifier.getIdentifier().getValue());
|
|
||||||
var valueIdentifier =
|
var valueIdentifier =
|
||||||
valueTypedIdentifier == null
|
valueParameter instanceof TypedIdentifier valueTypedIdentifier
|
||||||
? null
|
? valueTypedIdentifier
|
||||||
: toIdentifier(valueTypedIdentifier.getIdentifier().getValue());
|
: null;
|
||||||
if (valueIdentifier != null && valueIdentifier == keyIdentifier) {
|
if (keyIdentifier != null
|
||||||
|
&& valueIdentifier != null
|
||||||
|
&& keyIdentifier
|
||||||
|
.getIdentifier()
|
||||||
|
.getValue()
|
||||||
|
.equals(valueIdentifier.getIdentifier().getValue())) {
|
||||||
throw exceptionBuilder()
|
throw exceptionBuilder()
|
||||||
.evalError("duplicateDefinition", valueIdentifier)
|
.evalError("duplicateDefinition", valueIdentifier.getIdentifier().getValue())
|
||||||
.withSourceSection(createSourceSection(valueTypedIdentifier.getIdentifier()))
|
.withSourceSection(createSourceSection(valueIdentifier))
|
||||||
.build();
|
.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 =
|
var unresolvedKeyTypeNode =
|
||||||
keyTypedIdentifier == null
|
keyIdentifier == null ? null : visitTypeAnnotation(keyIdentifier.getTypeAnnotation());
|
||||||
? null
|
|
||||||
: visitTypeAnnotation(keyTypedIdentifier.getTypeAnnotation());
|
|
||||||
var unresolvedValueTypeNode =
|
var unresolvedValueTypeNode =
|
||||||
valueTypedIdentifier == null
|
valueIdentifier == null ? null : visitTypeAnnotation(valueIdentifier.getTypeAnnotation());
|
||||||
? null
|
|
||||||
: visitTypeAnnotation(valueTypedIdentifier.getTypeAnnotation());
|
|
||||||
// if possible, initialize immediately to avoid later insert
|
// if possible, initialize immediately to avoid later insert
|
||||||
var keyTypeNode =
|
var keyTypeNode =
|
||||||
unresolvedKeyTypeNode == null && keySlot != -1
|
unresolvedKeyTypeNode == null && keyBinding != null
|
||||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||||
.initWriteSlotNode(keySlot)
|
.initWriteSlotNode(keyBinding.slot())
|
||||||
: null;
|
: null;
|
||||||
// if possible, initialize immediately to avoid later insert
|
// if possible, initialize immediately to avoid later insert
|
||||||
var valueTypeNode =
|
var valueTypeNode =
|
||||||
unresolvedValueTypeNode == null && valueSlot != -1
|
unresolvedValueTypeNode == null && valueBinding != null
|
||||||
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
? new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection())
|
||||||
.initWriteSlotNode(valueSlot)
|
.initWriteSlotNode(valueBinding.slot())
|
||||||
: null;
|
: null;
|
||||||
var iterableNode = symbolTable.enterEagerGenerator(scope -> visitExpr(ctx.getExpr()));
|
var iterableNode = symbolTable.enterEagerGenerator(scope -> visitExpr(ctx.getExpr()));
|
||||||
var memberNodes =
|
var outerScope = symbolTable.getCurrentScope();
|
||||||
symbolTable.enterForGenerator(
|
return symbolTable.enterForGenerator(
|
||||||
params, generatorDescriptorBuilder, scope -> doVisitForWhenBody(ctx.getBody()));
|
keyBinding,
|
||||||
return GeneratorForNodeGen.create(
|
valueBinding,
|
||||||
createSourceSection(ctx),
|
symbolTable.getCurrentScope().frameDescriptorBuilder,
|
||||||
generatorDescriptorBuilder.build(),
|
scope -> {
|
||||||
iterableNode,
|
var memberNodes = doVisitForWhenBody(ctx.getBody());
|
||||||
unresolvedKeyTypeNode,
|
return GeneratorForNodeGen.create(
|
||||||
unresolvedValueTypeNode,
|
createSourceSection(ctx),
|
||||||
memberNodes,
|
scope.frameDescriptorBuilder.build(),
|
||||||
keyTypeNode,
|
iterableNode,
|
||||||
valueTypeNode);
|
unresolvedKeyTypeNode,
|
||||||
|
unresolvedValueTypeNode,
|
||||||
|
memberNodes,
|
||||||
|
keyTypeNode,
|
||||||
|
valueTypeNode,
|
||||||
|
keyBinding == null ? -1 : keyBinding.slot(),
|
||||||
|
valueBinding == null ? -1 : valueBinding.slot(),
|
||||||
|
outerScope.getForGeneratorSlots(),
|
||||||
|
scope.getParameterSlots());
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -1937,10 +1972,11 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
|
|
||||||
var bodyContext = entry.getExpr();
|
var bodyContext = entry.getExpr();
|
||||||
var paramListCtx = entry.getParameterList();
|
var paramListCtx = entry.getParameterList();
|
||||||
var descriptorBuilder = createFrameDescriptorBuilder(paramListCtx);
|
var descriptorBuilderAndBindings = createFrameDescriptorBuilderAndSlotVariables(paramListCtx);
|
||||||
var paramCount = paramListCtx.getParameters().size();
|
var paramCount = paramListCtx.getParameters().size();
|
||||||
|
|
||||||
var bindings = getParameterNames(paramListCtx);
|
var descriptorBuilder = descriptorBuilderAndBindings.first;
|
||||||
|
var bindings = descriptorBuilderAndBindings.second;
|
||||||
|
|
||||||
var annotations = doVisitAnnotations(entry.getAnnotations(), methodName);
|
var annotations = doVisitAnnotations(entry.getAnnotations(), methodName);
|
||||||
|
|
||||||
@@ -2221,8 +2257,14 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ExpressionNode doVisitObjectBody(ObjectBody body, ExpressionNode parentNode) {
|
private ExpressionNode doVisitObjectBody(ObjectBody body, ExpressionNode parentNode) {
|
||||||
|
var parametersDescriptorAndBindings = createFrameDescriptorBuilderAndSlotVariables(body);
|
||||||
|
var bindings =
|
||||||
|
parametersDescriptorAndBindings == null
|
||||||
|
? new FrameSlotVariable[0]
|
||||||
|
: parametersDescriptorAndBindings.second;
|
||||||
|
|
||||||
return symbolTable.enterObjectScope(
|
return symbolTable.enterObjectScope(
|
||||||
body,
|
bindings,
|
||||||
(scope) -> {
|
(scope) -> {
|
||||||
addObjectNamesToScope(scope, body);
|
addObjectNamesToScope(scope, body);
|
||||||
var objectMembers = body.getMembers();
|
var objectMembers = body.getMembers();
|
||||||
@@ -2232,7 +2274,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
}
|
}
|
||||||
var sourceSection = createSourceSection(body.parent());
|
var sourceSection = createSourceSection(body.parent());
|
||||||
|
|
||||||
var parametersDescriptorBuilder = createFrameDescriptorBuilder(body);
|
var parametersDescriptor =
|
||||||
|
parametersDescriptorAndBindings == null
|
||||||
|
? null
|
||||||
|
: parametersDescriptorAndBindings.first.build();
|
||||||
var parameterTypes = doVisitParameterTypes(body);
|
var parameterTypes = doVisitParameterTypes(body);
|
||||||
|
|
||||||
var members = EconomicMaps.<Object, ObjectMember>create();
|
var members = EconomicMaps.<Object, ObjectMember>create();
|
||||||
@@ -2278,8 +2323,6 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
var currentScope = symbolTable.getCurrentScope();
|
var currentScope = symbolTable.getCurrentScope();
|
||||||
var parametersDescriptor =
|
|
||||||
parametersDescriptorBuilder == null ? null : parametersDescriptorBuilder.build();
|
|
||||||
if (!elements.isEmpty()) {
|
if (!elements.isEmpty()) {
|
||||||
if (isConstantKeyNodes) { // true if zero key nodes
|
if (isConstantKeyNodes) { // true if zero key nodes
|
||||||
addConstantEntries(members, keyNodes, values);
|
addConstantEntries(members, keyNodes, values);
|
||||||
@@ -2516,8 +2559,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private ObjectMember doVisitObjectElement(ObjectElement element) {
|
private ObjectMember doVisitObjectElement(ObjectElement element) {
|
||||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
var outerScope = symbolTable.getCurrentScope();
|
||||||
return symbolTable.enterEntry(
|
var isForGeneratorScope = outerScope.isForGeneratorScope();
|
||||||
|
return symbolTable.enterEntryOrElement(
|
||||||
null,
|
null,
|
||||||
scope -> {
|
scope -> {
|
||||||
var elementNode = visitExpr(element.getExpr());
|
var elementNode = visitExpr(element.getExpr());
|
||||||
@@ -2535,7 +2579,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
member.initConstantValue(constantNode);
|
member.initConstantValue(constantNode);
|
||||||
} else {
|
} else {
|
||||||
if (isForGeneratorScope) {
|
if (isForGeneratorScope) {
|
||||||
elementNode = new RestoreForBindingsNode(elementNode);
|
elementNode =
|
||||||
|
new RestoreForBindingsNode(
|
||||||
|
elementNode, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||||
}
|
}
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
ElementOrEntryNodeGen.create(
|
ElementOrEntryNodeGen.create(
|
||||||
@@ -2586,9 +2632,10 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
|
|
||||||
var methodName = org.pkl.core.runtime.Identifier.method(identifier.getValue(), true);
|
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(
|
return symbolTable.enterMethod(
|
||||||
methodName,
|
methodName,
|
||||||
@@ -2629,7 +2676,8 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
|
|
||||||
private GeneratorObjectLiteralNode doVisitGeneratorObjectBody(
|
private GeneratorObjectLiteralNode doVisitGeneratorObjectBody(
|
||||||
ObjectBody body, ExpressionNode parentNode) {
|
ObjectBody body, ExpressionNode parentNode) {
|
||||||
var parametersDescriptor = createFrameDescriptorBuilder(body);
|
var parametersDescriptorBuilderAndFrameSlotVariables =
|
||||||
|
createFrameDescriptorBuilderAndSlotVariables(body);
|
||||||
var parameterTypes = doVisitParameterTypes(body);
|
var parameterTypes = doVisitParameterTypes(body);
|
||||||
var memberNodes = doVisitGeneratorMemberNodes(body.getMembers());
|
var memberNodes = doVisitGeneratorMemberNodes(body.getMembers());
|
||||||
var currentScope = symbolTable.getCurrentScope();
|
var currentScope = symbolTable.getCurrentScope();
|
||||||
@@ -2639,7 +2687,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
language,
|
language,
|
||||||
currentScope.getQualifiedName(),
|
currentScope.getQualifiedName(),
|
||||||
currentScope.isCustomThisScope(),
|
currentScope.isCustomThisScope(),
|
||||||
parametersDescriptor == null ? null : parametersDescriptor.build(),
|
parametersDescriptorBuilderAndFrameSlotVariables == null
|
||||||
|
? null
|
||||||
|
: parametersDescriptorBuilderAndFrameSlotVariables.first.build(),
|
||||||
parameterTypes,
|
parameterTypes,
|
||||||
memberNodes,
|
memberNodes,
|
||||||
parentNode);
|
parentNode);
|
||||||
@@ -2838,8 +2888,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
ExpressionNode keyNode,
|
ExpressionNode keyNode,
|
||||||
@Nullable Expr valueCtx,
|
@Nullable Expr valueCtx,
|
||||||
@Nullable List<? extends ObjectBody> objectBodyCtxs) {
|
@Nullable List<? extends ObjectBody> objectBodyCtxs) {
|
||||||
var isForGeneratorScope = symbolTable.getCurrentScope().isForGeneratorScope();
|
var outerScope = symbolTable.getCurrentScope();
|
||||||
return symbolTable.enterEntry(
|
var isForGeneratorScope = outerScope.isForGeneratorScope();
|
||||||
|
return symbolTable.enterEntryOrElement(
|
||||||
keyNode,
|
keyNode,
|
||||||
scope -> {
|
scope -> {
|
||||||
var modifier = VmModifier.ENTRY;
|
var modifier = VmModifier.ENTRY;
|
||||||
@@ -2856,7 +2907,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
member.initConstantValue(constantNode);
|
member.initConstantValue(constantNode);
|
||||||
} else {
|
} else {
|
||||||
if (isForGeneratorScope) {
|
if (isForGeneratorScope) {
|
||||||
valueNode = new RestoreForBindingsNode(valueNode);
|
valueNode =
|
||||||
|
new RestoreForBindingsNode(
|
||||||
|
valueNode, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||||
}
|
}
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
ElementOrEntryNodeGen.create(
|
ElementOrEntryNodeGen.create(
|
||||||
@@ -2869,7 +2922,9 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
objectBodyCtxs,
|
objectBodyCtxs,
|
||||||
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
new ReadSuperEntryNode(unavailableSourceSection(), new GetMemberKeyNode()));
|
||||||
if (isForGeneratorScope) {
|
if (isForGeneratorScope) {
|
||||||
objectBody = new RestoreForBindingsNode(objectBody);
|
objectBody =
|
||||||
|
new RestoreForBindingsNode(
|
||||||
|
objectBody, scope.getParameterSlots(), scope.getForGeneratorSlots());
|
||||||
}
|
}
|
||||||
member.initMemberNode(
|
member.initMemberNode(
|
||||||
ElementOrEntryNodeGen.create(
|
ElementOrEntryNodeGen.create(
|
||||||
@@ -2903,39 +2958,34 @@ public class AstBuilder extends AbstractAstBuilder<Object> {
|
|||||||
return needsConst;
|
return needsConst;
|
||||||
}
|
}
|
||||||
|
|
||||||
private static List<String> getParameterNames(ParameterList parameterList) {
|
private FrameSlotVariable[] getSlotVariables(
|
||||||
var result = new ArrayList<String>(parameterList.getParameters().size());
|
List<org.pkl.parser.syntax.Parameter> params, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||||
for (var param : parameterList.getParameters()) {
|
var slotVariables = new FrameSlotVariable[params.size()];
|
||||||
var name = param instanceof TypedIdentifier id ? id.getIdentifier().getValue() : "_";
|
for (var i = 0; i < params.size(); i++) {
|
||||||
result.add(name);
|
var param = params.get(i);
|
||||||
}
|
org.pkl.core.runtime.Identifier identifier;
|
||||||
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;
|
|
||||||
if (param instanceof TypedIdentifier typedIdentifier) {
|
if (param instanceof TypedIdentifier typedIdentifier) {
|
||||||
identifier = toIdentifier(typedIdentifier.getIdentifier().getValue());
|
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;
|
if (body.getParameters().isEmpty()) return null;
|
||||||
|
|
||||||
var builder = FrameDescriptor.newBuilder(body.getParameters().size());
|
var builder = new FrameDescriptorBuilder(body.getParameters().size());
|
||||||
for (var param : body.getParameters()) {
|
return Pair.of(builder, getSlotVariables(body.getParameters(), builder));
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void checkNotInsideForGenerator(Node ctx, String errorMessageKey) {
|
private void checkNotInsideForGenerator(Node ctx, String errorMessageKey) {
|
||||||
|
|||||||
@@ -15,6 +15,8 @@
|
|||||||
*/
|
*/
|
||||||
package org.pkl.core.ast.builder;
|
package org.pkl.core.ast.builder;
|
||||||
|
|
||||||
|
import static org.pkl.core.util.ArrayUtils.EMPTY_INT_ARRAY;
|
||||||
|
|
||||||
import com.oracle.truffle.api.frame.FrameDescriptor;
|
import com.oracle.truffle.api.frame.FrameDescriptor;
|
||||||
import java.util.*;
|
import java.util.*;
|
||||||
import java.util.function.Function;
|
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.ImplicitBaseMethod;
|
||||||
import org.pkl.core.ast.builder.MethodResolution.ImplicitThisMethod;
|
import org.pkl.core.ast.builder.MethodResolution.ImplicitThisMethod;
|
||||||
import org.pkl.core.ast.builder.MethodResolution.LexicalMethod;
|
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.ImplicitBaseProperty;
|
||||||
import org.pkl.core.ast.builder.VariableResolution.LexicalProperty;
|
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.ast.member.ObjectMember;
|
||||||
import org.pkl.core.runtime.BaseModuleMembers;
|
import org.pkl.core.runtime.BaseModuleMembers;
|
||||||
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
import org.pkl.core.runtime.FrameDescriptorBuilder;
|
||||||
|
import org.pkl.core.runtime.FrameSlotVariable;
|
||||||
import org.pkl.core.runtime.Identifier;
|
import org.pkl.core.runtime.Identifier;
|
||||||
import org.pkl.core.runtime.ModuleInfo;
|
import org.pkl.core.runtime.ModuleInfo;
|
||||||
import org.pkl.core.runtime.VmDataSize;
|
import org.pkl.core.runtime.VmDataSize;
|
||||||
import org.pkl.core.runtime.VmDuration;
|
import org.pkl.core.runtime.VmDuration;
|
||||||
|
import org.pkl.core.util.ArrayUtils;
|
||||||
import org.pkl.core.util.LateInit;
|
import org.pkl.core.util.LateInit;
|
||||||
import org.pkl.parser.Lexer;
|
import org.pkl.parser.Lexer;
|
||||||
import org.pkl.parser.syntax.ObjectBody;
|
|
||||||
|
|
||||||
public final class SymbolTable {
|
public final class SymbolTable {
|
||||||
|
|
||||||
@@ -94,7 +98,7 @@ public final class SymbolTable {
|
|||||||
public <T> T enterMethod(
|
public <T> T enterMethod(
|
||||||
Identifier name,
|
Identifier name,
|
||||||
ConstLevel constLevel,
|
ConstLevel constLevel,
|
||||||
List<String> bindings,
|
FrameSlotVariable[] bindings,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
List<TypeParameter> typeParameters,
|
List<TypeParameter> typeParameters,
|
||||||
Function<MethodScope, T> nodeFactory) {
|
Function<MethodScope, T> nodeFactory) {
|
||||||
@@ -115,17 +119,22 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> T enterForGenerator(
|
public <T> T enterForGenerator(
|
||||||
List<String> params,
|
@Nullable FrameSlotVariable keyBinding,
|
||||||
|
@Nullable FrameSlotVariable valueBinding,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
Function<ForGeneratorScope, T> nodeFactory) {
|
Function<ForGeneratorScope, T> nodeFactory) {
|
||||||
return doEnter(
|
return doEnter(
|
||||||
new ForGeneratorScope(
|
new ForGeneratorScope(
|
||||||
currentScope, currentScope.qualifiedName, params, frameDescriptorBuilder),
|
currentScope,
|
||||||
|
currentScope.qualifiedName,
|
||||||
|
keyBinding,
|
||||||
|
valueBinding,
|
||||||
|
frameDescriptorBuilder),
|
||||||
nodeFactory);
|
nodeFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T enterLambda(
|
public <T> T enterLambda(
|
||||||
List<String> bindings,
|
FrameSlotVariable[] bindings,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
Function<LambdaScope, T> nodeFactory) {
|
Function<LambdaScope, T> nodeFactory) {
|
||||||
|
|
||||||
@@ -144,7 +153,7 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public <T> T enterLetExpression(
|
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
|
// flatten names of let exprs inside other let exprs for presentation purposes
|
||||||
var parentScope = currentScope;
|
var parentScope = currentScope;
|
||||||
@@ -154,7 +163,7 @@ public final class SymbolTable {
|
|||||||
|
|
||||||
assert parentScope != null;
|
assert parentScope != null;
|
||||||
var qualifiedName = parentScope.qualifiedName + "." + "<let expr>";
|
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(
|
public <T> T enterProperty(
|
||||||
@@ -165,16 +174,16 @@ public final class SymbolTable {
|
|||||||
nodeFactory);
|
nodeFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T enterEntry(
|
public <T> T enterEntryOrElement(
|
||||||
@Nullable ExpressionNode keyNode, // null for listing elements
|
@Nullable ExpressionNode keyNode, // null for listing elements
|
||||||
Function<EntryScope, T> nodeFactory) {
|
Function<EntryOrElementScope, T> nodeFactory) {
|
||||||
|
|
||||||
var qualifiedName = currentScope.getQualifiedName() + currentScope.getNextEntryName(keyNode);
|
var qualifiedName = currentScope.getQualifiedName() + currentScope.getNextEntryName(keyNode);
|
||||||
var builder =
|
var builder =
|
||||||
currentScope instanceof ForGeneratorScope forScope
|
currentScope instanceof ForGeneratorScope forScope
|
||||||
? forScope.frameDescriptorBuilder
|
? forScope.frameDescriptorBuilder
|
||||||
: new 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) {
|
public <T> T enterCustomThisScope(Function<CustomThisScope, T> nodeFactory) {
|
||||||
@@ -193,9 +202,10 @@ public final class SymbolTable {
|
|||||||
nodeFactory);
|
nodeFactory);
|
||||||
}
|
}
|
||||||
|
|
||||||
public <T> T enterObjectScope(ObjectBody body, Function<ObjectScope, T> nodeFactory) {
|
public <T> T enterObjectScope(
|
||||||
|
FrameSlotVariable[] bindings, Function<ObjectScope, T> nodeFactory) {
|
||||||
return doEnter(
|
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) {
|
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;
|
protected final FrameDescriptorBuilder frameDescriptorBuilder;
|
||||||
private final ConstLevel constLevel;
|
private final ConstLevel constLevel;
|
||||||
protected boolean isBaseModule;
|
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
|
// The properties defined on this (lexical) scope
|
||||||
protected final Map<String, Member> properties = new HashMap<>();
|
protected final Map<String, Member> properties = new HashMap<>();
|
||||||
// The methods defined on this (lexical) scope
|
// The methods defined on this (lexical) scope
|
||||||
protected final Map<String, Member> methods = new HashMap<>();
|
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(
|
private Scope(
|
||||||
@Nullable Scope parent,
|
@Nullable Scope parent,
|
||||||
@Nullable Identifier name,
|
@Nullable Identifier name,
|
||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
ConstLevel constLevel,
|
ConstLevel constLevel,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
|
int[] forGeneratorSlots,
|
||||||
|
int[] parameterSlots) {
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.name = name;
|
this.name = name;
|
||||||
this.qualifiedName = qualifiedName;
|
this.qualifiedName = qualifiedName;
|
||||||
@@ -247,6 +275,8 @@ public final class SymbolTable {
|
|||||||
parent != null && parent.constLevel.biggerOrEquals(constLevel)
|
parent != null && parent.constLevel.biggerOrEquals(constLevel)
|
||||||
? parent.constLevel
|
? parent.constLevel
|
||||||
: constLevel;
|
: constLevel;
|
||||||
|
this.forGeneratorSlots = forGeneratorSlots;
|
||||||
|
this.parameterSlots = parameterSlots;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final @Nullable Scope getParent() {
|
public final @Nullable Scope getParent() {
|
||||||
@@ -266,16 +296,26 @@ public final class SymbolTable {
|
|||||||
return qualifiedName;
|
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
|
* Returns the parameter slots in this scope.
|
||||||
* descriptor.
|
*
|
||||||
|
* <p>Includes let bindings, object body params, method params, lambda params
|
||||||
*/
|
*/
|
||||||
public FrameDescriptorBuilder newFrameDescriptorBuilder() {
|
public int[] getParameterSlots() {
|
||||||
return new FrameDescriptorBuilder(buildFrameDescriptor());
|
return parameterSlots;
|
||||||
|
}
|
||||||
|
|
||||||
|
public FrameDescriptor buildFrameDescriptor() {
|
||||||
|
return frameDescriptorBuilder.build();
|
||||||
}
|
}
|
||||||
|
|
||||||
public @Nullable TypeParameter getTypeParameter(String name) {
|
public @Nullable TypeParameter getTypeParameter(String name) {
|
||||||
@@ -389,6 +429,13 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public final boolean isCustomThisScope() {
|
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;
|
return this instanceof CustomThisScope;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -505,21 +552,45 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static class ObjectScope extends Scope implements LexicalScope {
|
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(
|
private ObjectScope(
|
||||||
Scope parent, ObjectBody body, FrameDescriptorBuilder frameDescriptorBuilder) {
|
Scope parent, FrameSlotVariable[] bindings, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||||
super(
|
super(
|
||||||
parent,
|
parent,
|
||||||
parent.getNameOrNull(),
|
parent.getNameOrNull(),
|
||||||
parent.getQualifiedName(),
|
parent.getQualifiedName(),
|
||||||
ConstLevel.NONE,
|
ConstLevel.NONE,
|
||||||
frameDescriptorBuilder);
|
frameDescriptorBuilder,
|
||||||
params = collectParams(body);
|
parent.forGeneratorSlots,
|
||||||
|
EMPTY_INT_ARRAY);
|
||||||
|
this.bindings = bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean hasParams() {
|
public boolean hasParams() {
|
||||||
return !params.isEmpty();
|
return bindings.length > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -532,10 +603,11 @@ public final class SymbolTable {
|
|||||||
if (prop != null) {
|
if (prop != null) {
|
||||||
return new VariableResolution.LexicalProperty(false, prop.modifiers, levelsUp);
|
return new VariableResolution.LexicalProperty(false, prop.modifiers, levelsUp);
|
||||||
}
|
}
|
||||||
var paramIndex = params.get(name);
|
for (var binding : bindings) {
|
||||||
if (paramIndex != null) {
|
if (binding.name().equals(name)) {
|
||||||
// params are on a higher level than the properties
|
// params are on a higher level than the properties
|
||||||
return new VariableResolution.Parameter(paramIndex, levelsUp + 1);
|
return new VariableResolution.Parameter(binding.slot(), levelsUp + 1);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -548,19 +620,6 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
return null;
|
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 {
|
public abstract static class TypeParameterizableScope extends Scope {
|
||||||
@@ -572,8 +631,17 @@ public final class SymbolTable {
|
|||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
ConstLevel constLevel,
|
ConstLevel constLevel,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
List<TypeParameter> typeParameters) {
|
List<TypeParameter> typeParameters,
|
||||||
super(parent, name, qualifiedName, constLevel, frameDescriptorBuilder);
|
int[] forGeneratorSlots,
|
||||||
|
int[] parameterSlots) {
|
||||||
|
super(
|
||||||
|
parent,
|
||||||
|
name,
|
||||||
|
qualifiedName,
|
||||||
|
constLevel,
|
||||||
|
frameDescriptorBuilder,
|
||||||
|
forGeneratorSlots,
|
||||||
|
parameterSlots);
|
||||||
this.typeParameters = typeParameters;
|
this.typeParameters = typeParameters;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -593,7 +661,14 @@ public final class SymbolTable {
|
|||||||
private final boolean isAmend;
|
private final boolean isAmend;
|
||||||
|
|
||||||
public ModuleScope(ModuleInfo moduleInfo, boolean isBaseModule) {
|
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.isBaseModule = isBaseModule;
|
||||||
this.moduleInfo = moduleInfo;
|
this.moduleInfo = moduleInfo;
|
||||||
this.isAmend = moduleInfo.isAmend();
|
this.isAmend = moduleInfo.isAmend();
|
||||||
@@ -622,17 +697,25 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class MethodScope extends TypeParameterizableScope implements LexicalScope {
|
public static final class MethodScope extends TypeParameterizableScope implements LexicalScope {
|
||||||
private final List<String> bindings;
|
private final FrameSlotVariable[] bindings;
|
||||||
|
|
||||||
public MethodScope(
|
MethodScope(
|
||||||
Scope parent,
|
Scope parent,
|
||||||
Identifier name,
|
Identifier name,
|
||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
ConstLevel constLevel,
|
ConstLevel constLevel,
|
||||||
List<String> bindings,
|
FrameSlotVariable[] bindings,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
List<TypeParameter> typeParameters) {
|
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;
|
this.bindings = bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -648,14 +731,21 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class LambdaScope extends Scope implements LexicalScope {
|
public static final class LambdaScope extends Scope implements LexicalScope {
|
||||||
private final List<String> bindings;
|
private final FrameSlotVariable[] bindings;
|
||||||
|
|
||||||
public LambdaScope(
|
public LambdaScope(
|
||||||
Scope parent,
|
Scope parent,
|
||||||
List<String> bindings,
|
FrameSlotVariable[] bindings,
|
||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||||
super(parent, null, qualifiedName, parent.getConstLevel(), frameDescriptorBuilder);
|
super(
|
||||||
|
parent,
|
||||||
|
null,
|
||||||
|
qualifiedName,
|
||||||
|
parent.getConstLevel(),
|
||||||
|
frameDescriptorBuilder,
|
||||||
|
EMPTY_INT_ARRAY,
|
||||||
|
getSlots(bindings));
|
||||||
this.bindings = bindings;
|
this.bindings = bindings;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -671,8 +761,8 @@ public final class SymbolTable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static final class LetExpressionScope extends Scope implements LexicalScope {
|
public static final class LetExpressionScope extends Scope implements LexicalScope {
|
||||||
private final @Nullable String binding;
|
public static final Object LET_BINDING_SLOT = new Object();
|
||||||
private final int slot;
|
private final @Nullable FrameSlotVariable binding;
|
||||||
|
|
||||||
private static @Nullable Identifier getParentName(Scope parent) {
|
private static @Nullable Identifier getParentName(Scope parent) {
|
||||||
while (parent != null && parent.name == null) {
|
while (parent != null && parent.name == null) {
|
||||||
@@ -681,25 +771,34 @@ public final class SymbolTable {
|
|||||||
return parent == null ? null : parent.name;
|
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(
|
public LetExpressionScope(
|
||||||
Scope parent, @Nullable String binding, int slot, String qualifiedName) {
|
Scope parent, @Nullable FrameSlotVariable binding, String qualifiedName) {
|
||||||
super(
|
super(
|
||||||
parent,
|
parent,
|
||||||
getParentName(parent),
|
getParentName(parent),
|
||||||
qualifiedName,
|
qualifiedName,
|
||||||
parent.getConstLevel(),
|
parent.getConstLevel(),
|
||||||
parent.frameDescriptorBuilder);
|
parent.frameDescriptorBuilder,
|
||||||
|
parent.forGeneratorSlots,
|
||||||
|
getMyParameterSlots(parent, binding));
|
||||||
this.binding = binding;
|
this.binding = binding;
|
||||||
this.slot = slot;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
||||||
if (name.equals("_")) {
|
if (name.equals("_") || binding == null) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
if (name.equals(binding)) {
|
if (name.equals(binding.name())) {
|
||||||
return new ForGeneratorOrLetVariable(slot, levelsUp);
|
return new ForGeneratorVariableOrLetBinding(binding.slot(), levelsUp);
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -718,21 +817,87 @@ public final class SymbolTable {
|
|||||||
return grandParent.frameDescriptorBuilder;
|
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) {
|
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 {
|
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(
|
public ForGeneratorScope(
|
||||||
Scope parent,
|
Scope parent,
|
||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
List<String> params,
|
@Nullable FrameSlotVariable keyBinding,
|
||||||
|
@Nullable FrameSlotVariable valueBinding,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||||
super(parent, null, qualifiedName, ConstLevel.NONE, frameDescriptorBuilder);
|
super(
|
||||||
this.params = params;
|
parent,
|
||||||
|
null,
|
||||||
|
qualifiedName,
|
||||||
|
ConstLevel.NONE,
|
||||||
|
frameDescriptorBuilder,
|
||||||
|
getMyForGeneratorSlots(parent, keyBinding, valueBinding),
|
||||||
|
getMyParameterSlots(parent));
|
||||||
|
this.keyBinding = keyBinding;
|
||||||
|
this.valueBinding = valueBinding;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -744,12 +909,11 @@ public final class SymbolTable {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
public @Nullable VariableResolution doResolveProperty(String name, int levelsUp) {
|
||||||
if (!params.contains(name)) {
|
if (keyBinding != null && keyBinding.name().equals(name)) {
|
||||||
return null;
|
return new ForGeneratorVariableOrLetBinding(keyBinding.slot(), levelsUp);
|
||||||
}
|
}
|
||||||
var index = frameDescriptorBuilder.findSlot(Identifier.get(name));
|
if (valueBinding != null && valueBinding.name().equals(name)) {
|
||||||
if (index >= 0) {
|
return new ForGeneratorVariableOrLetBinding(valueBinding.slot(), levelsUp);
|
||||||
return new ForGeneratorOrLetVariable(index, levelsUp);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@@ -767,14 +931,30 @@ public final class SymbolTable {
|
|||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
ConstLevel constLevel,
|
ConstLevel constLevel,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder) {
|
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 static final class EntryOrElementScope extends Scope {
|
||||||
public EntryScope(
|
public EntryOrElementScope(
|
||||||
Scope parent, String qualifiedName, FrameDescriptorBuilder frameDescriptorBuilder) {
|
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,
|
int modifiers,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
List<TypeParameter> typeParameters) {
|
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);
|
isClosed = VmModifier.isClosed(modifiers);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -816,7 +1004,15 @@ public final class SymbolTable {
|
|||||||
String qualifiedName,
|
String qualifiedName,
|
||||||
FrameDescriptorBuilder frameDescriptorBuilder,
|
FrameDescriptorBuilder frameDescriptorBuilder,
|
||||||
List<TypeParameter> typeParameters) {
|
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.getNameOrNull(),
|
||||||
parent.getQualifiedName(),
|
parent.getQualifiedName(),
|
||||||
ConstLevel.NONE,
|
ConstLevel.NONE,
|
||||||
frameDescriptorBuilder);
|
frameDescriptorBuilder,
|
||||||
|
parent.forGeneratorSlots,
|
||||||
|
parent.parameterSlots);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -849,18 +1047,22 @@ public final class SymbolTable {
|
|||||||
public AnnotationScope(
|
public AnnotationScope(
|
||||||
Scope parent, String qualifiedName, FrameDescriptorBuilder frameDescriptorBuilder) {
|
Scope parent, String qualifiedName, FrameDescriptorBuilder frameDescriptorBuilder) {
|
||||||
super(
|
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(
|
private static @Nullable VariableResolution resolveParameter(
|
||||||
String name, List<String> bindings, int levelsUp) {
|
String name, FrameSlotVariable[] bindings, int levelsUp) {
|
||||||
if (name.equals("_")) {
|
for (var binding : bindings) {
|
||||||
return null;
|
if (name.equals(binding.name())) {
|
||||||
}
|
return new Parameter(binding.slot(), levelsUp);
|
||||||
var index = bindings.indexOf(name);
|
}
|
||||||
if (index != -1) {
|
|
||||||
return new VariableResolution.Parameter(index, levelsUp);
|
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ public sealed interface VariableResolution {
|
|||||||
// method, lambda, object body param
|
// method, lambda, object body param
|
||||||
record Parameter(int slot, int levelsUp) implements VariableResolution {}
|
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
|
// Implicit base module lookup
|
||||||
record ImplicitBaseProperty() implements VariableResolution {}
|
record ImplicitBaseProperty() implements VariableResolution {}
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ public abstract class LetExprNode extends ExpressionNode {
|
|||||||
typeNode = new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection());
|
typeNode = new TypeNode.UnknownTypeNode(VmUtils.unavailableSourceSection());
|
||||||
}
|
}
|
||||||
typeNode.initWriteSlotNode(slot);
|
typeNode.initWriteSlotNode(slot);
|
||||||
|
frame.getFrameDescriptor().setSlotKind(slot, typeNode.getFrameSlotKind());
|
||||||
insert(typeNode);
|
insert(typeNode);
|
||||||
}
|
}
|
||||||
assert typeNode != null;
|
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.TypeNode;
|
||||||
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
import org.pkl.core.ast.type.UnresolvedTypeNode;
|
||||||
import org.pkl.core.runtime.*;
|
import org.pkl.core.runtime.*;
|
||||||
|
import org.pkl.core.util.ArrayUtils;
|
||||||
|
|
||||||
public abstract class GeneratorForNode extends GeneratorMemberNode {
|
public abstract class GeneratorForNode extends GeneratorMemberNode {
|
||||||
private final FrameDescriptor generatorDescriptor;
|
private final FrameDescriptor generatorDescriptor;
|
||||||
@@ -37,6 +38,9 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
|||||||
@Children private final GeneratorMemberNode[] childNodes;
|
@Children private final GeneratorMemberNode[] childNodes;
|
||||||
@Child private @Nullable TypeNode keyTypeNode;
|
@Child private @Nullable TypeNode keyTypeNode;
|
||||||
@Child private @Nullable TypeNode valueTypeNode;
|
@Child private @Nullable TypeNode valueTypeNode;
|
||||||
|
private final int keySlot;
|
||||||
|
private final int valueSlot;
|
||||||
|
private final int[] slotsToCopy;
|
||||||
|
|
||||||
public GeneratorForNode(
|
public GeneratorForNode(
|
||||||
SourceSection sourceSection,
|
SourceSection sourceSection,
|
||||||
@@ -52,7 +56,11 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
|||||||
@Nullable TypeNode keyTypeNode,
|
@Nullable TypeNode keyTypeNode,
|
||||||
// If this node can be constructed at parse time,
|
// If this node can be constructed at parse time,
|
||||||
// it should be passed instead of `unresolvedValueTypeNode`.
|
// it should be passed instead of `unresolvedValueTypeNode`.
|
||||||
@Nullable TypeNode valueTypeNode) {
|
@Nullable TypeNode valueTypeNode,
|
||||||
|
int keySlot,
|
||||||
|
int valueSlot,
|
||||||
|
int[] outerForGeneratorSlots,
|
||||||
|
int[] parameterSlots) {
|
||||||
super(sourceSection, false);
|
super(sourceSection, false);
|
||||||
this.generatorDescriptor = generatorDescriptor;
|
this.generatorDescriptor = generatorDescriptor;
|
||||||
this.iterableNode = iterableNode;
|
this.iterableNode = iterableNode;
|
||||||
@@ -61,6 +69,9 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
|||||||
this.childNodes = childNodes;
|
this.childNodes = childNodes;
|
||||||
this.keyTypeNode = keyTypeNode;
|
this.keyTypeNode = keyTypeNode;
|
||||||
this.valueTypeNode = valueTypeNode;
|
this.valueTypeNode = valueTypeNode;
|
||||||
|
this.keySlot = keySlot;
|
||||||
|
this.valueSlot = valueSlot;
|
||||||
|
this.slotsToCopy = ArrayUtils.concat(parameterSlots, outerForGeneratorSlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract void executeWithIterable(
|
protected abstract void executeWithIterable(
|
||||||
@@ -154,12 +165,10 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
|||||||
VirtualFrame frame, Object parent, ObjectData data, Object key, Object value) {
|
VirtualFrame frame, Object parent, ObjectData data, Object key, Object value) {
|
||||||
|
|
||||||
// GraalJS uses the same implementation technique here:
|
// GraalJS uses the same implementation technique here:
|
||||||
// https://github.com/oracle/graaljs/blob/44a11ce6e87/graal-js/src/com.oracle.truffle.js/
|
// 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
|
||||||
// src/com/oracle/truffle/js/nodes/function/IterationScopeNode.java#L86-L88
|
|
||||||
var newFrame =
|
var newFrame =
|
||||||
Truffle.getRuntime().createVirtualFrame(frame.getArguments(), generatorDescriptor);
|
Truffle.getRuntime().createVirtualFrame(frame.getArguments(), generatorDescriptor);
|
||||||
// the locals in `frame` (if any) are function arguments and/or outer for-generator bindings
|
VmUtils.copyLocals(frame, newFrame, slotsToCopy);
|
||||||
VmUtils.copyLocals(frame, 0, newFrame, 0, frame.getFrameDescriptor().getNumberOfSlots());
|
|
||||||
if (keyTypeNode != null) {
|
if (keyTypeNode != null) {
|
||||||
keyTypeNode.executeAndSet(newFrame, key);
|
keyTypeNode.executeAndSet(newFrame, key);
|
||||||
}
|
}
|
||||||
@@ -175,14 +184,12 @@ public abstract class GeneratorForNode extends GeneratorMemberNode {
|
|||||||
private void initialize(VirtualFrame frame) {
|
private void initialize(VirtualFrame frame) {
|
||||||
if (unresolvedKeyTypeNode != null) {
|
if (unresolvedKeyTypeNode != null) {
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
var keySlot = frame.getFrameDescriptor().getNumberOfSlots();
|
|
||||||
keyTypeNode = insert(unresolvedKeyTypeNode.execute(frame)).initWriteSlotNode(keySlot);
|
keyTypeNode = insert(unresolvedKeyTypeNode.execute(frame)).initWriteSlotNode(keySlot);
|
||||||
generatorDescriptor.setSlotKind(keySlot, keyTypeNode.getFrameSlotKind());
|
generatorDescriptor.setSlotKind(keySlot, keyTypeNode.getFrameSlotKind());
|
||||||
unresolvedKeyTypeNode = null;
|
unresolvedKeyTypeNode = null;
|
||||||
}
|
}
|
||||||
if (unresolvedValueTypeNode != null) {
|
if (unresolvedValueTypeNode != null) {
|
||||||
CompilerDirectives.transferToInterpreterAndInvalidate();
|
CompilerDirectives.transferToInterpreterAndInvalidate();
|
||||||
var valueSlot = frame.getFrameDescriptor().getNumberOfSlots() + (keyTypeNode != null ? 1 : 0);
|
|
||||||
valueTypeNode = insert(unresolvedValueTypeNode.execute(frame)).initWriteSlotNode(valueSlot);
|
valueTypeNode = insert(unresolvedValueTypeNode.execute(frame)).initWriteSlotNode(valueSlot);
|
||||||
generatorDescriptor.setSlotKind(valueSlot, valueTypeNode.getFrameSlotKind());
|
generatorDescriptor.setSlotKind(valueSlot, valueTypeNode.getFrameSlotKind());
|
||||||
unresolvedValueTypeNode = null;
|
unresolvedValueTypeNode = null;
|
||||||
|
|||||||
+6
-7
@@ -18,27 +18,26 @@ package org.pkl.core.ast.expression.generator;
|
|||||||
import com.oracle.truffle.api.frame.VirtualFrame;
|
import com.oracle.truffle.api.frame.VirtualFrame;
|
||||||
import org.pkl.core.ast.ExpressionNode;
|
import org.pkl.core.ast.ExpressionNode;
|
||||||
import org.pkl.core.runtime.VmUtils;
|
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.
|
* Restores for-generator variable bindings when a member generated by a for-generator is executed.
|
||||||
*/
|
*/
|
||||||
public final class RestoreForBindingsNode extends ExpressionNode {
|
public final class RestoreForBindingsNode extends ExpressionNode {
|
||||||
private @Child ExpressionNode child;
|
private @Child ExpressionNode child;
|
||||||
|
private final int[] slotsToCopy;
|
||||||
|
|
||||||
public RestoreForBindingsNode(ExpressionNode child) {
|
public RestoreForBindingsNode(
|
||||||
|
ExpressionNode child, int[] parameterSlots, int[] forGeneratorSlots) {
|
||||||
super(child.getSourceSection());
|
super(child.getSourceSection());
|
||||||
this.child = child;
|
this.child = child;
|
||||||
|
this.slotsToCopy = ArrayUtils.concat(parameterSlots, forGeneratorSlots);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object executeGeneric(VirtualFrame frame) {
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
var generatorFrame = ObjectData.getGeneratorFrame(frame);
|
var generatorFrame = ObjectData.getGeneratorFrame(frame);
|
||||||
// copying all slots includes function arguments, but the capture generator frame
|
VmUtils.copyLocals(generatorFrame, frame, slotsToCopy);
|
||||||
// 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());
|
|
||||||
return child.executeGeneric(frame);
|
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) {
|
public Object executeGeneric(VirtualFrame frame) {
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
|
|
||||||
var levelsUp = 0;
|
var levelsUp = -1;
|
||||||
for (var current = VmUtils.getOwner(frame).getEnclosingOwner();
|
for (var current = VmUtils.getOwner(frame);
|
||||||
current != null;
|
current != null;
|
||||||
current = current.getEnclosingOwner()) {
|
current = current.getEnclosingOwner()) {
|
||||||
if (!current.isParseTimeInvisibleScope()) {
|
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);
|
var parameters = CollectionUtils.<String, PType>newLinkedHashMap(paramCount);
|
||||||
for (var i = 0; i < paramCount; i++) {
|
for (var i = 0; i < paramCount; i++) {
|
||||||
var slotName = getFrameDescriptor().getSlotName(i);
|
var slotName = getFrameDescriptor().getSlotName(i);
|
||||||
// Ignored parameters (`_`) have no name
|
var paramName = slotName == Identifier.ILLEGAL ? "_#" + i : slotName.toString();
|
||||||
var paramName = slotName == null ? "_#" + i : slotName.toString();
|
|
||||||
parameters.put(paramName, TypeNode.export(parameterTypeNodes[i]));
|
parameters.put(paramName, TypeNode.export(parameterTypeNodes[i]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,7 @@ import com.oracle.truffle.api.frame.FrameSlotKind;
|
|||||||
import java.util.Arrays;
|
import java.util.Arrays;
|
||||||
import org.jspecify.annotations.Nullable;
|
import org.jspecify.annotations.Nullable;
|
||||||
|
|
||||||
/**
|
/** A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also gives us the current size. */
|
||||||
* A wrapper for Truffle's {@link FrameDescriptor.Builder}, but also lets us find the slot of a
|
|
||||||
* given {@link Identifier}.
|
|
||||||
*/
|
|
||||||
public class FrameDescriptorBuilder {
|
public class FrameDescriptorBuilder {
|
||||||
|
|
||||||
private @Nullable Identifier[] names;
|
private @Nullable Identifier[] names;
|
||||||
@@ -42,16 +39,6 @@ public class FrameDescriptorBuilder {
|
|||||||
this.names = new Identifier[capacity];
|
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) {
|
private void ensureCapacity(int count) {
|
||||||
if (names.length < size + count) {
|
if (names.length < size + count) {
|
||||||
var newLength = Math.max(size + count, size * 2);
|
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);
|
ensureCapacity(1);
|
||||||
names[size] = name;
|
names[size] = name;
|
||||||
size++;
|
size++;
|
||||||
return underlying.addSlot(kind, name, info);
|
var slot = underlying.addSlot(kind, name, info);
|
||||||
}
|
return new FrameSlotVariable(name.toString(), slot);
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public FrameDescriptor build() {
|
public FrameDescriptor build() {
|
||||||
return underlying.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 GLOB = get("glob");
|
||||||
public static final Identifier COMPLETION_CANDIDATES = get("completionCandidates");
|
public static final Identifier COMPLETION_CANDIDATES = get("completionCandidates");
|
||||||
|
|
||||||
|
public static final Identifier ILLEGAL = get("`");
|
||||||
|
|
||||||
// common in lambdas etc
|
// common in lambdas etc
|
||||||
public static final Identifier IT = get("it");
|
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
|
* Copies the slots specified by {@code slotsToCopy} locals from {@code sourceFrame} into {@code
|
||||||
* firstSourceSlot}, to {@code firstSourceSlot}, starting at {@code firstTargetSlot}.
|
* targetFrame}.
|
||||||
*/
|
*/
|
||||||
public static void copyLocals(
|
public static void copyLocals(
|
||||||
VirtualFrame sourceFrame,
|
VirtualFrame sourceFrame, VirtualFrame targetFrame, int[] slotsToCopy) {
|
||||||
int firstSourceSlot,
|
if (slotsToCopy.length == 0) return;
|
||||||
VirtualFrame targetFrame,
|
|
||||||
int firstTargetSlot,
|
|
||||||
int numberOfLocalsToCopy) {
|
|
||||||
var sourceDescriptor = sourceFrame.getFrameDescriptor();
|
var sourceDescriptor = sourceFrame.getFrameDescriptor();
|
||||||
var targetDescriptor = targetFrame.getFrameDescriptor();
|
var targetDescriptor = targetFrame.getFrameDescriptor();
|
||||||
assert sourceDescriptor.getNumberOfSlots() <= targetDescriptor.getNumberOfSlots();
|
for (var slot : slotsToCopy) {
|
||||||
// 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,
|
// If, for a particular call site of this method,
|
||||||
// slot kinds of `sourceDescriptor` will reach a steady state,
|
// slot kinds of `sourceDescriptor` will reach a steady state,
|
||||||
// then slot kinds of `targetDescriptor` will too.
|
// then slot kinds of `targetDescriptor` will too.
|
||||||
var slotKind = sourceDescriptor.getSlotKind(sourceSlot);
|
var slotKind = sourceDescriptor.getSlotKind(slot);
|
||||||
switch (slotKind) {
|
switch (slotKind) {
|
||||||
case Boolean -> {
|
case Boolean -> {
|
||||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Boolean);
|
targetDescriptor.setSlotKind(slot, FrameSlotKind.Boolean);
|
||||||
targetFrame.setBoolean(targetSlot, sourceFrame.getBoolean(sourceSlot));
|
targetFrame.setBoolean(slot, sourceFrame.getBoolean(slot));
|
||||||
}
|
}
|
||||||
case Long -> {
|
case Long -> {
|
||||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Long);
|
targetDescriptor.setSlotKind(slot, FrameSlotKind.Long);
|
||||||
targetFrame.setLong(targetSlot, sourceFrame.getLong(sourceSlot));
|
targetFrame.setLong(slot, sourceFrame.getLong(slot));
|
||||||
}
|
}
|
||||||
case Double -> {
|
case Double -> {
|
||||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Double);
|
targetDescriptor.setSlotKind(slot, FrameSlotKind.Double);
|
||||||
targetFrame.setDouble(targetSlot, sourceFrame.getDouble(sourceSlot));
|
targetFrame.setDouble(slot, sourceFrame.getDouble(slot));
|
||||||
}
|
}
|
||||||
case Object -> {
|
case Object -> {
|
||||||
targetDescriptor.setSlotKind(targetSlot, FrameSlotKind.Object);
|
targetDescriptor.setSlotKind(slot, FrameSlotKind.Object);
|
||||||
targetFrame.setObject(
|
targetFrame.setObject(
|
||||||
targetSlot,
|
slot,
|
||||||
sourceFrame instanceof MaterializedFrame
|
sourceFrame instanceof MaterializedFrame
|
||||||
// Even though sourceDescriptor.getSlotKind is now Object,
|
// Even though sourceDescriptor.getSlotKind is now Object,
|
||||||
// it may have been a primitive kind when `sourceFrame`'s local was written.
|
// 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().
|
// Hence, we need to read the local with getValue() instead of getObject().
|
||||||
? sourceFrame.getValue(sourceSlot)
|
? sourceFrame.getValue(slot)
|
||||||
: sourceFrame.getObject(sourceSlot));
|
: sourceFrame.getObject(slot));
|
||||||
}
|
}
|
||||||
default -> {
|
default -> {
|
||||||
CompilerDirectives.transferToInterpreter();
|
CompilerDirectives.transferToInterpreter();
|
||||||
@@ -978,10 +970,9 @@ public final class VmUtils {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static int findCustomThisSlot(VirtualFrame frame) {
|
public static int findCustomThisSlot(VirtualFrame frame) {
|
||||||
return frame
|
var result = frame.getFrameDescriptor().getAuxiliarySlots().get(CustomThisScope.FRAME_SLOT_ID);
|
||||||
.getFrameDescriptor()
|
assert result != null;
|
||||||
.getAuxiliarySlots()
|
return result;
|
||||||
.getOrDefault(CustomThisScope.FRAME_SLOT_ID, -1);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@TruffleBoundary
|
@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
|
[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
+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
|
// nested predicate
|
||||||
res6 = (people) {
|
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
|
age = 55
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -86,3 +87,28 @@ res12 = (people) {
|
|||||||
[[name == "Pigeon"]] { age = 122 }
|
[[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
|
20
|
||||||
40
|
40
|
||||||
}
|
}
|
||||||
|
nestedForsWithinLambda {
|
||||||
|
"hi"
|
||||||
|
new {
|
||||||
|
2
|
||||||
|
3
|
||||||
|
}
|
||||||
|
"hi"
|
||||||
|
new {
|
||||||
|
3
|
||||||
|
4
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
+29
@@ -323,3 +323,32 @@ withinLetExpr {
|
|||||||
["a"] = 1
|
["a"] = 1
|
||||||
["b"] = 2
|
["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
|
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