diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index b53cd49..fa441f5 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -64,8 +64,10 @@ public class Instruction { GLOB_SET(0x61), GLOB_DEF(0x62), - STACK_ALLOC(0x70), - STACK_REALLOC(0x71); + // CAP_INIT(0x70), + VAR_INIT(0x71), + CAP_FREE(0x72), + VAR_FREE(0x73); private static final HashMap types = new HashMap<>(); public final int numeric; @@ -409,11 +411,11 @@ public class Instruction { return new Instruction(Type.DUP, count, offset); } - public static Instruction storeVar(int i) { - return new Instruction(Type.STORE_VAR, i, false); - } - public static Instruction storeVar(int i, boolean keep) { - return new Instruction(Type.STORE_VAR, i, keep); + // public static Instruction storeVar(int i) { + // return new Instruction(Type.STORE_VAR, i, false); + // } + public static Instruction storeVar(int i, boolean keep, boolean initialize) { + return new Instruction(Type.STORE_VAR, i, keep, initialize); } public static Instruction storeMember() { @@ -463,12 +465,21 @@ public class Instruction { return new Instruction(Type.OPERATION, op); } - public static Instruction stackAlloc(int start, int n) { - return new Instruction(Type.STACK_ALLOC, start, start + n); + public static Instruction capFree(int i) { + return new Instruction(Type.CAP_FREE, i); } - public static Instruction stackRealloc(int start, int n) { - return new Instruction(Type.STACK_REALLOC, start, start + n); + public static Instruction varFree(int i) { + return new Instruction(Type.VAR_FREE, i); } + public static Instruction varInit(int i, boolean force) { + return new Instruction(Type.VAR_INIT, i, force); + } + // public static Instruction stackAlloc(int start, int n) { + // return new Instruction(Type.STACK_ALLOC, start, start + n); + // } + // public static Instruction stackRealloc(int start, int n) { + // return new Instruction(Type.STACK_REALLOC, start, start + n); + // } @Override public String toString() { var res = type.toString(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 60e86e8..a346738 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -78,6 +78,31 @@ public final class CompileResult { setLocationAndDebug(instructions.size() - 1, loc, type); } + public void beginScope() { + // for (var cap : scope.capturables()) { + // add(_i -> Instruction.capInit(cap.index().index)); + // } + } + public void reallocScope() { + for (var cap : scope.capturables()) { + add(_i -> cap.index().toGet()); + add(_i -> Instruction.capFree(cap.index().index)); + add(_i -> cap.index().toInit()); + } + + scope.end(); + } + public void endScope() { + for (var cap : scope.capturables()) { + add(_i -> Instruction.capFree(cap.index().index)); + } + for (var var : scope.locals()) { + add(_i -> Instruction.varFree(var.index().index)); + } + + scope.end(); + } + public int addChild(CompileResult res) { this.children.add(res); return this.children.size() - 1; @@ -106,7 +131,7 @@ public final class CompileResult { for (var suppl : instructions) { instrRes[i] = suppl.apply(i); - // System.out.println(instrRes[i]); + System.out.println(instrRes[i]); i++; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index f32edbf..b99a75f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -25,10 +25,7 @@ public class CompoundNode extends Node { List statements = new ArrayList(); var subtarget = hasScope ? target.subtarget() : target; - if (hasScope) { - subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); - subtarget.scope.singleEntry = singleEntry; - } + if (hasScope) subtarget.beginScope(); for (var stm : this.statements) { if (stm instanceof FunctionStatementNode func) { @@ -46,7 +43,7 @@ public class CompoundNode extends Node { else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); } - if (hasScope) subtarget.scope.end(); + if (hasScope) subtarget.endScope(); if (!polluted && pollute) { target.add(Instruction.pushUndefined()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index e37a9cd..f5a54e2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -50,7 +50,7 @@ public abstract class FunctionNode extends Node { var i = scope.defineSpecial(new Variable(selfName, true), end); target.add(Instruction.loadCallee()); - target.add(_i -> i.index().toSet(false)); + target.add(_i -> i.index().toInit()); } body.resolve(target); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 550730a..6de7691 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -18,7 +18,8 @@ public class FunctionStatementNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); - target.add(VariableNode.toSet(target, end, this.name, pollute, true)); + target.add(VariableNode.toInit(target, end, this.name)); + if (pollute) target.add(Instruction.pushUndefined()); } public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index d762e8e..85e3d0f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -37,8 +37,8 @@ public class VariableDeclareNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.value == null) { - if (declType == DeclarationType.VAR) entry.destructor.declare(target, null); - else entry.destructor.declare(target, declType); + if (declType == DeclarationType.VAR) entry.destructor.declare(target, null, false); + else entry.destructor.declare(target, declType, false); } else { entry.value.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index b331267..454cfb3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; -import me.topchetoeu.jscript.compilation.scope.Variable; -import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.patterns.Binding; public class ForInNode extends Node { - public final String varName; - public final DeclarationType declType; + public final Binding binding; public final Node object, body; public final String label; - public final Location varLocation; @Override public void resolve(CompileResult target) { body.resolve(target); - if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), loc()); + binding.resolve(target); } @Override public void compile(CompileResult target, boolean pollute) { - if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation); + binding.declareLateInit(target); object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(false, true)); @@ -38,8 +34,8 @@ public class ForInNode extends Node { target.add(Instruction.dup()); int mid = target.temp(); - target.add(Instruction.loadMember("value")).setLocation(varLocation); - target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict)); + target.add(Instruction.loadMember("value")).setLocation(binding.loc()); + binding.assign(target, false); target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); var end = new DeferredIntSupplier(); @@ -56,12 +52,10 @@ public class ForInNode extends Node { if (pollute) target.add(Instruction.pushUndefined()); } - public ForInNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) { + public ForInNode(Location loc, String label, Binding binding, Node object, Node body) { super(loc); - this.varLocation = varLocation; this.label = label; - this.declType = declType; - this.varName = varName; + this.binding = binding; this.object = object; this.body = body; } @@ -82,13 +76,9 @@ public class ForInNode extends Node { n++; n += Parsing.skipEmpty(src, i + n); - var declType = JavaScript.parseDeclarationType(src, i + n); - n += declType.n; - - var name = Parsing.parseIdentifier(src, i + n); - if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-in loop"); - var nameLoc = src.loc(i + n); - n += name.n; + var binding = Binding.parse(src, i + n); + if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop"); + n += binding.n; n += Parsing.skipEmpty(src, i + n); if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); @@ -106,6 +96,6 @@ public class ForInNode extends Node { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; - return ParseRes.res(new ForInNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForInNode(loc, label.result, binding.result, obj.result, bodyRes.result), n); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java index 933ec14..2b78601 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -26,7 +26,7 @@ public class ForNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { var subtarget = target.subtarget(); subtarget.scope.singleEntry = false; - subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); + subtarget.beginScope(); declaration.compile(subtarget, false, BreakpointType.STEP_OVER); @@ -40,7 +40,7 @@ public class ForNode extends Node { CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); LabelContext.popLoop(subtarget.env, label); - subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); + subtarget.reallocScope(); CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); int endI = subtarget.size(); @@ -51,7 +51,7 @@ public class ForNode extends Node { subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); if (pollute) subtarget.add(Instruction.pushUndefined()); - subtarget.scope.end(); + subtarget.endScope(); } public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index b675e7b..6438c9f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; -import me.topchetoeu.jscript.compilation.scope.Variable; -import me.topchetoeu.jscript.compilation.values.VariableNode; +import me.topchetoeu.jscript.compilation.patterns.Binding; public class ForOfNode extends Node { - public final String varName; - public final DeclarationType declType; + public final Binding binding; public final Node iterable, body; public final String label; - public final Location varLocation; @Override public void resolve(CompileResult target) { body.resolve(target); - if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), varLocation); + binding.resolve(target); } @Override public void compile(CompileResult target, boolean pollute) { - if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation); + binding.declareLateInit(target); iterable.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.dup()); @@ -46,8 +42,8 @@ public class ForOfNode extends Node { target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); int mid = target.temp(); - target.add(Instruction.loadMember("value")).setLocation(varLocation); - target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict)); + target.add(Instruction.loadMember("value")).setLocation(binding.loc); + binding.assign(target, false); var end = new DeferredIntSupplier(); @@ -65,12 +61,10 @@ public class ForOfNode extends Node { if (pollute) target.add(Instruction.pushUndefined()); } - public ForOfNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) { + public ForOfNode(Location loc, String label, Binding binding, Node object, Node body) { super(loc); - this.varLocation = varLocation; this.label = label; - this.declType = declType; - this.varName = varName; + this.binding = binding; this.iterable = object; this.body = body; } @@ -91,13 +85,9 @@ public class ForOfNode extends Node { n++; n += Parsing.skipEmpty(src, i + n); - var declType = JavaScript.parseDeclarationType(src, i + n); - n += declType.n; - - var name = Parsing.parseIdentifier(src, i + n); - if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a variable name for for-of loop"); - var nameLoc = src.loc(i + n); - n += name.n; + var binding = Binding.parse(src, i + n); + if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop"); + n += binding.n; n += Parsing.skipEmpty(src, i + n); if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); @@ -115,6 +105,6 @@ public class ForOfNode extends Node { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); n += bodyRes.n; - return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForOfNode(loc, label.result, binding.result, obj.result, bodyRes.result), n); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index e8a729b..ee00222 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -44,7 +44,7 @@ public class SwitchNode extends Node { value.compile(target, true, BreakpointType.STEP_OVER); var subtarget = target.subtarget(); - subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); + subtarget.beginScope(); // TODO: create a jump map for (var ccase : cases) { @@ -64,7 +64,7 @@ public class SwitchNode extends Node { } LabelContext.getBreak(target.env).pop(label); - subtarget.scope.end(); + subtarget.endScope(); int endI = subtarget.size(); end.set(endI); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java index fffb14e..18700b3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -43,13 +43,14 @@ public class TryNode extends Node { if (captureName != null) { var subtarget = target.subtarget(); - subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); + subtarget.beginScope(); subtarget.scope.singleEntry = true; var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc()); subtarget.add(Instruction.loadError()); - subtarget.add(_i -> catchVar.index().toSet(false)); + subtarget.add(catchVar.index().toInit()); catchBody.compile(subtarget, false); + subtarget.endScope(); subtarget.scope.end(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java index c4d1c11..e6ea21a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java @@ -14,19 +14,17 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; public class AssignPattern implements Pattern { public final Location loc; - public final Pattern assignable; + public final AssignTarget assignable; public final Node value; @Override public Location loc() { return loc; } @Override public void destructDeclResolve(CompileResult target) { - assignable.destructDeclResolve(target); + if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + p.destructDeclResolve(target); } - @Override public void declare(CompileResult target, DeclarationType decl) { - throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration"); - } - @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { + private void common(CompileResult target) { target.add(Instruction.dup()); target.add(Instruction.pushUndefined()); target.add(Instruction.operation(Operation.EQUALS)); @@ -36,8 +34,27 @@ public class AssignPattern implements Pattern { value.compile(target, true); target.set(start, Instruction.jmpIfNot(target.size() - start)); + } - assignable.destruct(target, decl, shouldDeclare); + @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { + if (lateInitializer) { + if (assignable instanceof Pattern p) p.declare(target, decl, lateInitializer); + else throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + } + else throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration"); + } + @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { + if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + common(target); + p.destruct(target, decl, shouldDeclare); + } + + @Override public void beforeAssign(CompileResult target) { + assignable.beforeAssign(target); + } + @Override public void afterAssign(CompileResult target, boolean pollute) { + common(target); + assignable.afterAssign(target, false); } public AssignPattern(Location loc, Pattern assignable, Node value) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java new file mode 100644 index 0000000..bf7524f --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java @@ -0,0 +1,80 @@ +package me.topchetoeu.jscript.compilation.patterns; + +import me.topchetoeu.jscript.common.SyntaxException; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.common.parsing.ParseRes; +import me.topchetoeu.jscript.common.parsing.Parsing; +import me.topchetoeu.jscript.common.parsing.Source; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.JavaScript; +import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; + +public class Binding implements Pattern { + public final Location loc; + public final DeclarationType type; + public final AssignTarget assignable; + + @Override public Location loc() { return loc; } + + @Override public void destructDeclResolve(CompileResult target) { + if (type != null && !type.strict) { + if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + p.destructDeclResolve(target); + } + } + + @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { + if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + p.destruct(target, decl, shouldDeclare); + } + @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { + if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); + p.declare(target, decl, lateInitializer); + } + + public void resolve(CompileResult target) { + if (type != null) destructDeclResolve(target); + } + + public void declare(CompileResult target, boolean hasInit) { + if (type != null) destructVar(target, type, hasInit); + } + public void declareLateInit(CompileResult target) { + if (type != null) declare(target, type, true); + } + + @Override public void afterAssign(CompileResult target, boolean pollute) { + assignable.assign(target, pollute); + } + + public Binding(Location loc, DeclarationType type, AssignTarget assignable) { + this.loc = loc; + this.type = type; + this.assignable = assignable; + } + + public static ParseRes parse(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + var loc = src.loc(i + n); + + var declType = JavaScript.parseDeclarationType(src, i + n); + if (!declType.isSuccess()) { + var res = JavaScript.parseExpression(src, i + n, 13); + if (res.isSuccess() && res.result instanceof AssignTargetLike target) { + n += res.n; + return ParseRes.res(new Binding(loc, null, target.toAssignTarget()), n); + } + else return ParseRes.failed(); + } + else { + n += declType.n; + n += Parsing.skipEmpty(src, i + n); + + var res = Pattern.parse(src, i + n, false); + if (!res.isSuccess()) return ParseRes.failed(); + n += res.n; + + return ParseRes.res(new Binding(loc, declType.result, res.result), n); + } + } +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectAssignable.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectAssignable.java deleted file mode 100644 index f3abc5e..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectAssignable.java +++ /dev/null @@ -1,16 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import java.util.List; - -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.CompileResult; - -public class ObjectAssignable extends ObjectDestructor implements AssignTarget { - @Override public void afterAssign(CompileResult target, boolean pollute) { - compile(target, t -> t.assign(target, false), pollute); - } - - public ObjectAssignable(Location loc, List> members) { - super(loc, members); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectDestructor.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectDestructor.java deleted file mode 100644 index de7b51f..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectDestructor.java +++ /dev/null @@ -1,44 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import java.util.List; -import java.util.function.Consumer; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.values.operations.IndexNode; - -public abstract class ObjectDestructor extends Node { - public static final class Member { - public final Node key; - public final T consumable; - - public Member(Node key, T consumer) { - this.key = key; - this.consumable = consumer; - } - } - - public final List> members; - - public void consume(Consumer consumer) { - for (var el : members) { - consumer.accept(el.consumable); - } - } - public void compile(CompileResult target, Consumer consumer, boolean pollute) { - for (var el : members) { - target.add(Instruction.dup()); - IndexNode.indexLoad(target, el.key, true); - consumer.accept(el.consumable); - } - - if (!pollute) target.add(Instruction.discard()); - } - - public ObjectDestructor(Location loc, List> members) { - super(loc); - this.members = members; - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java index ca395fa..65e508d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java @@ -2,36 +2,79 @@ package me.topchetoeu.jscript.compilation.patterns; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; +import me.topchetoeu.jscript.compilation.values.operations.IndexNode; + +public class ObjectPattern extends Node implements Pattern { + public static final class Member { + public final Node key; + public final AssignTarget consumable; + + public Member(Node key, AssignTarget consumer) { + this.key = key; + this.consumable = consumer; + } + } + + public final List members; + + public void compile(CompileResult target, Consumer consumer, boolean pollute) { + for (var el : members) { + target.add(Instruction.dup()); + IndexNode.indexLoad(target, el.key, true); + consumer.accept(el.consumable); + } + + if (!pollute) target.add(Instruction.discard()); + } -public class ObjectPattern extends ObjectDestructor implements Pattern { @Override public void destructDeclResolve(CompileResult target) { - consume(t -> t.destructDeclResolve(target)); + for (var t : members) { + if (t.consumable instanceof Pattern p) p.destructDeclResolve(target); + else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context"); + } } @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { - compile(target, t -> t.destruct(target, decl, shouldDeclare), false); + compile(target, t -> { + if (t instanceof Pattern p) p.destruct(target, decl, shouldDeclare); + else throw new SyntaxException(t.loc(), "Unexpected non-pattern in destruct context"); + }, false); } - @Override public void declare(CompileResult target, DeclarationType decl) { - throw new SyntaxException(loc(), "Object pattern must be initialized"); + @Override public void afterAssign(CompileResult target, boolean pollute) { + compile(target, t -> t.assign(target, false), pollute); } - public ObjectPattern(Location loc, List> members) { - super(loc, members); + @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { + if (lateInitializer) { + for (var t : members) { + if (t.consumable instanceof Pattern p) p.declare(target, decl, lateInitializer); + else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context"); + } + } + else throw new SyntaxException(loc(), "Object pattern must be initialized"); } - private static ParseRes> parseShorthand(Source src, int i) { + public ObjectPattern(Location loc, List members) { + super(loc); + this.members = members; + } + + private static ParseRes parseShorthand(Source src, int i) { ParseRes res = ParseRes.first(src, i, AssignPattern::parse, VariableNode::parse @@ -40,17 +83,17 @@ public class ObjectPattern extends ObjectDestructor implements Pattern if (res.isSuccess()) { if (res.result instanceof AssignPattern assign) { if (assign.assignable instanceof VariableNode var) { - return ParseRes.res(new Member<>(new StringNode(var.loc(), var.name), res.result), res.n); + return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n); } } else if (res.result instanceof VariableNode var) { - return ParseRes.res(new Member<>(new StringNode(var.loc(), var.name), res.result), res.n); + return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n); } } return res.chainError(); } - private static ParseRes> parseKeyed(Source src, int i) { + private static ParseRes parseKeyed(Source src, int i) { var n = Parsing.skipEmpty(src, i); var key = ObjectNode.parsePropName(src, i + n); @@ -65,7 +108,7 @@ public class ObjectPattern extends ObjectDestructor implements Pattern if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon"); n += res.n; - return ParseRes.res(new Member<>(key.result, res.result), n); + return ParseRes.res(new Member(key.result, res.result), n); } public static ParseRes parse(Source src, int i) { @@ -76,7 +119,7 @@ public class ObjectPattern extends ObjectDestructor implements Pattern n++; n += Parsing.skipEmpty(src, i + n); - var members = new LinkedList>(); + var members = new LinkedList(); if (src.is(i + n, "}")) { n++; @@ -84,7 +127,7 @@ public class ObjectPattern extends ObjectDestructor implements Pattern } while (true) { - ParseRes> prop = ParseRes.first(src, i + n, + ParseRes prop = ParseRes.first(src, i + n, ObjectPattern::parseKeyed, ObjectPattern::parseShorthand ); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java index 44a4ad6..07d07fb 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java @@ -10,7 +10,7 @@ import me.topchetoeu.jscript.compilation.values.VariableNode; /** * Represents all nodes that can be a destructors (note that all destructors are assign targets, too) */ -public interface Pattern { +public interface Pattern extends AssignTarget { Location loc(); /** @@ -28,7 +28,21 @@ public interface Pattern { /** * Run when destructing a declaration without an initializer */ - void declare(CompileResult target, DeclarationType decl); + void declare(CompileResult target, DeclarationType decl, boolean lateInitializer); + + public default void destructArg(CompileResult target, DeclarationType decl) { + destruct(target, decl, false); + } + public default void destructVar(CompileResult target, DeclarationType decl, boolean hasInitializer) { + if (hasInitializer) { + if (decl == null || !decl.strict) destruct(target, null, true); + else destruct(target, decl, true); + } + else { + if (decl == null || !decl.strict) declare(target, null, false); + else declare(target, decl, false); + } + } public static ParseRes parse(Source src, int i, boolean withDefault) { return withDefault ? diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index 5daf8aa..c5319bc 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -36,7 +36,7 @@ public class FunctionScope extends Scope { } else { functionVarMap.put(var.name, var); - return variables.add(var); + return locals.add(var); } } public Variable defineSpecial(Variable var, Location loc) { @@ -44,7 +44,7 @@ public class FunctionScope extends Scope { if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name); specialVarMap.put(var.name, var); - return variables.add(var); + return locals.add(var); } @Override public Variable get(String name, boolean capture) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java index e848e40..bed8f93 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -10,8 +10,8 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; public class Scope { protected final HashMap strictVarMap = new HashMap<>(); - protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset); - protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset); + protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset); + protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset); private boolean ended = false; private boolean finished = false; @@ -29,7 +29,7 @@ public class Scope { protected void transferCaptured(Variable var) { if (!singleEntry) { - this.captured.add(var); + this.capturables.add(var); } else if (parent != null) { parent.transferCaptured(var); @@ -60,7 +60,7 @@ public class Scope { */ public Variable defineTemp() { checkNotEnded(); - return this.variables.add(new Variable("", false)); + return this.locals.add(new Variable("", false)); } /** @@ -96,7 +96,7 @@ public class Scope { if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name); strictVarMap.put(var.name, var); - return variables.add(var); + return locals.add(var); } /** * Gets the index supplier of the given variable name, or null if it is a global @@ -129,7 +129,7 @@ public class Scope { var res = 0; for (var curr = parent; curr != null; curr = curr.parent) { - res += parent.variables.size(); + res += parent.locals.size(); } return res; @@ -138,7 +138,7 @@ public class Scope { var res = 0; for (var curr = this; curr != null; curr = curr.parent) { - if (curr != this) res += parent.captured.size(); + if (curr != this) res += parent.capturables.size(); if (curr.parent == null) res += curr.localsCount(); } @@ -157,19 +157,26 @@ public class Scope { if (res < childN) res = childN; } - return res + variables.size(); + return res + locals.size(); } public int capturesCount() { return 0; } public int allocCount() { - var res = captured.size(); + var res = capturables.size(); return res; } public int capturablesCount() { - var res = captured.size(); + var res = capturables.size(); for (var child : children) res += child.capturablesCount(); return res; } + public Iterable capturables() { + return capturables.all(); + } + public Iterable locals() { + return locals.all(); + } + /** * Ends this scope. This will make it possible for another child to take its place */ @@ -187,8 +194,8 @@ public class Scope { } protected void onFinish() { - this.variables.freeze(); - this.captured.freeze(); + this.locals.freeze(); + this.capturables.freeze(); } /** diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java index 3e04a05..374d7b1 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java @@ -30,9 +30,25 @@ public final class VariableIndex { } public final Instruction toSet(boolean keep) { switch (type) { - case CAPTURES: return Instruction.storeVar(~index, keep); - case CAPTURABLES: return Instruction.storeVar(index, keep); - case LOCALS: return Instruction.storeVar(index, keep); + case CAPTURES: return Instruction.storeVar(index, keep, false); + case CAPTURABLES: return Instruction.storeVar(index, keep, false); + case LOCALS: return Instruction.storeVar(index, keep, false); + default: throw new UnsupportedOperationException("Unknown index type " + type); + } + } + public final Instruction toInit() { + switch (type) { + case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type); + case CAPTURABLES: return Instruction.storeVar(index, false, true); + case LOCALS: return Instruction.storeVar(index, false, true); + default: throw new UnsupportedOperationException("Unknown index type " + type); + } + } + public final Instruction toUndefinedInit(boolean force) { + switch (type) { + case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type); + case CAPTURABLES: return Instruction.varInit(index, force); + case LOCALS: return Instruction.varInit(index, force); default: throw new UnsupportedOperationException("Unknown index type " + type); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 02b770f..670a680 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -19,9 +19,8 @@ import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Parameters; import me.topchetoeu.jscript.compilation.patterns.AssignTarget; import me.topchetoeu.jscript.compilation.patterns.AssignTargetLike; -import me.topchetoeu.jscript.compilation.patterns.ObjectAssignable; +import me.topchetoeu.jscript.compilation.patterns.ObjectPattern; import me.topchetoeu.jscript.compilation.patterns.Pattern; -import me.topchetoeu.jscript.compilation.patterns.ObjectDestructor.Member; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; import me.topchetoeu.jscript.compilation.values.operations.AssignNode; @@ -296,18 +295,18 @@ public class ObjectNode extends Node implements AssignTargetLike { } @Override public AssignTarget toAssignTarget() { - var newMembers = new LinkedList>(); + var newMembers = new LinkedList(); for (var el : members) { if (el instanceof FieldMemberNode field) { - if (field.value instanceof AssignTargetLike target) newMembers.add(new Member<>(field.key, target.toAssignTarget())); + if (field.value instanceof AssignTargetLike target) newMembers.add(new ObjectPattern.Member(field.key, target.toAssignTarget())); else throw new SyntaxException(field.value.loc(), "Expected an assignable in deconstructor"); } - else if (el instanceof AssignShorthandNode shorthand) newMembers.add(new Member<>(shorthand.key, shorthand.target())); + else if (el instanceof AssignShorthandNode shorthand) newMembers.add(new ObjectPattern.Member(shorthand.key, shorthand.target())); else throw new SyntaxException(el.loc(), "Unexpected member in deconstructor"); } - return new ObjectAssignable(loc(), newMembers); + return new ObjectPattern(loc(), newMembers); } public ObjectNode(Location loc, List map) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index 17f8bcf..bd0a9f8 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -26,17 +26,18 @@ public class VariableNode extends Node implements Pattern, ChangeTarget { } @Override public void destructDeclResolve(CompileResult target) { - target.scope.define(new Variable(name, false), loc()); + var i = target.scope.define(new Variable(name, false), loc()); + if (i != null) target.add(_i -> i.index().toUndefinedInit(false)); } @Override public void afterAssign(CompileResult target, boolean pollute) { - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute)); } - @Override public void declare(CompileResult target, DeclarationType decl) { + @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { if (decl != null) { - if (decl.strict) target.scope.defineStrict(new Variable(name, decl.readonly), loc()); - else target.scope.define(new Variable(name, decl.readonly), loc()); + var i = target.scope.define(decl, name, loc()); + target.add(_i -> i.index().toUndefinedInit(decl.strict)); } else target.add(_i -> { var i = target.scope.get(name, false); @@ -48,52 +49,60 @@ public class VariableNode extends Node implements Pattern, ChangeTarget { @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { if (!shouldDeclare || decl == null) { - target.add(VariableNode.toSet(target, loc(), name, false, shouldDeclare)); + if (shouldDeclare) target.add(VariableNode.toInit(target, loc(), name)); + else target.add(VariableNode.toInit(target, loc(), name)); } else { if (decl == DeclarationType.VAR && target.scope.has(name, false)) throw new SyntaxException(loc(), "Duplicate parameter name not allowed"); var v = target.scope.define(decl, name, loc()); - target.add(_i -> v.index().toSet(false)); + target.add(_i -> v.index().toInit()); } } @Override public void compile(CompileResult target, boolean pollute) { - var i = target.scope.get(name, false); - - if (i == null) { - target.add(_i -> { - if (target.scope.has(name, false)) return Instruction.throwSyntax(loc(), String.format("Cannot access '%s' before initialization", name)); - return Instruction.globGet(name, false); - }); - - if (!pollute) target.add(Instruction.discard()); - } - else if (pollute) target.add(_i -> i.index().toGet()); + target.add(toGet(target, loc(), name, true, false)); } - public static IntFunction toGet(CompileResult target, Location loc, String name, boolean forceGet) { - var i = target.scope.get(name, false); + public static IntFunction toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) { + var oldI = target.scope.get(name, true); - if (i == null) return _i -> { - if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); - else return Instruction.globGet(name, forceGet); + if (oldI != null) { + if (keep) return _i -> oldI.index().toGet(); + else return _i -> Instruction.nop(); + } + else return _i -> { + var newI = target.scope.get(name, false); + + if (newI == null) return Instruction.globGet(name, forceGet); + else if (keep) return newI.index().toGet(); + else return Instruction.nop(); }; - else return _i -> i.index().toGet(); } public static IntFunction toGet(CompileResult target, Location loc, String name) { - return toGet(target, loc, name, false); + return toGet(target, loc, name, true, false); } + public static IntFunction toInit(CompileResult target, Location loc, String name) { + var oldI = target.scope.get(name, true); - public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { - var i = target.scope.get(name, false); + if (oldI != null) return _i -> oldI.index().toInit(); + else return _i -> { + var i = target.scope.get(name, false); - if (i == null) return _i -> { - if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); - else return Instruction.globSet(name, keep, define); + if (i == null) return Instruction.globSet(name, false, true); + else return i.index().toInit(); + }; + } + public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep) { + var oldI = target.scope.get(name, true); + + if (oldI != null) return _i -> oldI.index().toSet(keep); + else return _i -> { + var i = target.scope.get(name, false); + + if (i == null) return Instruction.globSet(name, keep, false); + else return i.index().toSet(keep); }; - else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable")); - else return _i -> i.index().toSet(keep); } public VariableNode(Location loc, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java index 19b3798..ba89965 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java @@ -16,7 +16,7 @@ public class TypeofNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { if (value instanceof VariableNode varNode) { - target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true)); + target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true)); if (pollute) target.add(Instruction.typeof()); else target.add(Instruction.discard()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index 940a27c..9f11b5b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -18,11 +18,11 @@ public class VariableAssignNode extends Node { target.add(VariableNode.toGet(target, loc(), name)); FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.operation(operation)); - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute)); } else { FunctionNode.compileWithName(value, target, true, name); - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute)); } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index 4fc615a..17e5a2c 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,6 +1,5 @@ package me.topchetoeu.jscript.runtime; -import java.util.Arrays; import java.util.Stack; import java.util.concurrent.CancellationException; @@ -361,15 +360,7 @@ public final class Frame { this.argsVal = new ArgumentsValue(this, args); this.captures = func.captures; - var i = 0; - this.locals = new Value[func.body.localsN]; - Arrays.fill(locals, Value.UNDEFINED); - this.capturables = new Value[func.body.capturablesN][1]; - - for (i = 0; i < func.body.capturablesN; i++) { - this.capturables[i][0] = Value.UNDEFINED; - } } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 1fa9ecf..a019369 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -90,7 +90,7 @@ public class InstructionRunner { var members = new ArrayList<>(val.getMembers(env, instr.get(0), instr.get(1))); Collections.reverse(members); - frame.push(null); + frame.push(Value.UNDEFINED); for (var el : members) { var obj = new ObjectValue(); @@ -147,7 +147,9 @@ public class InstructionRunner { private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { int i = instr.get(0); - frame.push(frame.getVar(i)); + var res = frame.getVar(i); + if (res == null) throw EngineException.ofSyntax("Uninitialized variable"); + frame.push(res); frame.codePtr++; return null; @@ -285,6 +287,7 @@ public class InstructionRunner { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); int i = instr.get(0); + if (!(boolean)instr.get(2) && frame.getVar(i) == null) throw EngineException.ofSyntax("Uninitialized variable"); frame.setVar(i, val); frame.codePtr++; @@ -516,21 +519,21 @@ public class InstructionRunner { return null; } - private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) { - int offset = instr.get(0); - int n = instr.get(1); - - for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { Value.UNDEFINED }; + private static Value execVarInit(Environment env, Instruction instr, Frame frame) { + if ((boolean)instr.get(1) || frame.getVar(instr.get(0)) == null) { + frame.setVar(instr.get(0), Value.UNDEFINED); + } frame.codePtr++; return null; } - private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) { - int offset = instr.get(0); - int n = instr.get(1); - - for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { frame.capturables[i][0] }; - + private static Value execVarFree(Environment env, Instruction instr, Frame frame) { + frame.locals[(int)instr.get(0)] = null; + frame.codePtr++; + return null; + } + private static Value execCapFree(Environment env, Instruction instr, Frame frame) { + frame.capturables[(int)instr.get(0) - frame.locals.length] = new Value[1]; frame.codePtr++; return null; } @@ -592,8 +595,9 @@ public class InstructionRunner { case GLOB_GET: return exexGlobGet(env, instr, frame); case GLOB_SET: return exexGlobSet(env, instr, frame); - case STACK_ALLOC: return execStackAlloc(env, instr, frame); - case STACK_REALLOC: return execStackRealloc(env, instr, frame); + case VAR_INIT: return execVarInit(env, instr, frame); + case VAR_FREE: return execVarFree(env, instr, frame); + case CAP_FREE: return execCapFree(env, instr, frame); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); } diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index 28185d9..eb274d7 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -1,3 +1,5 @@ +return; + const target = arguments[0]; const primordials = arguments[1];