fix: retrofit patterns for bindings and check if var is init in runtime

This commit is contained in:
TopchetoEU 2024-09-19 11:02:02 +03:00
parent fbbd26bf7d
commit 8a21873631
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
27 changed files with 374 additions and 237 deletions

View File

@ -64,8 +64,10 @@ public class Instruction {
GLOB_SET(0x61), GLOB_SET(0x61),
GLOB_DEF(0x62), GLOB_DEF(0x62),
STACK_ALLOC(0x70), // CAP_INIT(0x70),
STACK_REALLOC(0x71); VAR_INIT(0x71),
CAP_FREE(0x72),
VAR_FREE(0x73);
private static final HashMap<Integer, Type> types = new HashMap<>(); private static final HashMap<Integer, Type> types = new HashMap<>();
public final int numeric; public final int numeric;
@ -409,11 +411,11 @@ public class Instruction {
return new Instruction(Type.DUP, count, offset); return new Instruction(Type.DUP, count, offset);
} }
public static Instruction storeVar(int i) { // public static Instruction storeVar(int i) {
return new Instruction(Type.STORE_VAR, i, false); // return new Instruction(Type.STORE_VAR, i, false);
} // }
public static Instruction storeVar(int i, boolean keep) { public static Instruction storeVar(int i, boolean keep, boolean initialize) {
return new Instruction(Type.STORE_VAR, i, keep); return new Instruction(Type.STORE_VAR, i, keep, initialize);
} }
public static Instruction storeMember() { public static Instruction storeMember() {
@ -463,12 +465,21 @@ public class Instruction {
return new Instruction(Type.OPERATION, op); return new Instruction(Type.OPERATION, op);
} }
public static Instruction stackAlloc(int start, int n) { public static Instruction capFree(int i) {
return new Instruction(Type.STACK_ALLOC, start, start + n); return new Instruction(Type.CAP_FREE, i);
} }
public static Instruction stackRealloc(int start, int n) { public static Instruction varFree(int i) {
return new Instruction(Type.STACK_REALLOC, start, start + n); 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() { @Override public String toString() {
var res = type.toString(); var res = type.toString();

View File

@ -78,6 +78,31 @@ public final class CompileResult {
setLocationAndDebug(instructions.size() - 1, loc, type); 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) { public int addChild(CompileResult res) {
this.children.add(res); this.children.add(res);
return this.children.size() - 1; return this.children.size() - 1;
@ -106,7 +131,7 @@ public final class CompileResult {
for (var suppl : instructions) { for (var suppl : instructions) {
instrRes[i] = suppl.apply(i); instrRes[i] = suppl.apply(i);
// System.out.println(instrRes[i]); System.out.println(instrRes[i]);
i++; i++;
} }

View File

@ -25,10 +25,7 @@ public class CompoundNode extends Node {
List<Node> statements = new ArrayList<Node>(); List<Node> statements = new ArrayList<Node>();
var subtarget = hasScope ? target.subtarget() : target; var subtarget = hasScope ? target.subtarget() : target;
if (hasScope) { if (hasScope) subtarget.beginScope();
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
subtarget.scope.singleEntry = singleEntry;
}
for (var stm : this.statements) { for (var stm : this.statements) {
if (stm instanceof FunctionStatementNode func) { if (stm instanceof FunctionStatementNode func) {
@ -46,7 +43,7 @@ public class CompoundNode extends Node {
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
} }
if (hasScope) subtarget.scope.end(); if (hasScope) subtarget.endScope();
if (!polluted && pollute) { if (!polluted && pollute) {
target.add(Instruction.pushUndefined()); target.add(Instruction.pushUndefined());

View File

@ -50,7 +50,7 @@ public abstract class FunctionNode extends Node {
var i = scope.defineSpecial(new Variable(selfName, true), end); var i = scope.defineSpecial(new Variable(selfName, true), end);
target.add(Instruction.loadCallee()); target.add(Instruction.loadCallee());
target.add(_i -> i.index().toSet(false)); target.add(_i -> i.index().toInit());
} }
body.resolve(target); body.resolve(target);

View File

@ -18,7 +18,8 @@ public class FunctionStatementNode extends FunctionNode {
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null)); var id = target.addChild(compileBody(target, name, null));
target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); 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) { public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {

View File

@ -37,8 +37,8 @@ public class VariableDeclareNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
for (var entry : values) { for (var entry : values) {
if (entry.value == null) { if (entry.value == null) {
if (declType == DeclarationType.VAR) entry.destructor.declare(target, null); if (declType == DeclarationType.VAR) entry.destructor.declare(target, null, false);
else entry.destructor.declare(target, declType); else entry.destructor.declare(target, declType, false);
} }
else { else {
entry.value.compile(target, true); entry.value.compile(target, true);

View File

@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.LabelContext;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.patterns.Binding;
import me.topchetoeu.jscript.compilation.scope.Variable;
import me.topchetoeu.jscript.compilation.values.VariableNode;
public class ForInNode extends Node { public class ForInNode extends Node {
public final String varName; public final Binding binding;
public final DeclarationType declType;
public final Node object, body; public final Node object, body;
public final String label; public final String label;
public final Location varLocation;
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
body.resolve(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) { @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); object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys(false, true)); target.add(Instruction.keys(false, true));
@ -38,8 +34,8 @@ public class ForInNode extends Node {
target.add(Instruction.dup()); target.add(Instruction.dup());
int mid = target.temp(); int mid = target.temp();
target.add(Instruction.loadMember("value")).setLocation(varLocation); target.add(Instruction.loadMember("value")).setLocation(binding.loc());
target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict)); binding.assign(target, false);
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
var end = new DeferredIntSupplier(); var end = new DeferredIntSupplier();
@ -56,12 +52,10 @@ public class ForInNode extends Node {
if (pollute) target.add(Instruction.pushUndefined()); 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); super(loc);
this.varLocation = varLocation;
this.label = label; this.label = label;
this.declType = declType; this.binding = binding;
this.varName = varName;
this.object = object; this.object = object;
this.body = body; this.body = body;
} }
@ -82,13 +76,9 @@ public class ForInNode extends Node {
n++; n++;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
var declType = JavaScript.parseDeclarationType(src, i + n); var binding = Binding.parse(src, i + n);
n += declType.n; if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop");
n += binding.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;
n += Parsing.skipEmpty(src, i + 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"); 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"); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body");
n += bodyRes.n; 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);
} }
} }

View File

@ -26,7 +26,7 @@ public class ForNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
var subtarget = target.subtarget(); var subtarget = target.subtarget();
subtarget.scope.singleEntry = false; subtarget.scope.singleEntry = false;
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); subtarget.beginScope();
declaration.compile(subtarget, false, BreakpointType.STEP_OVER); declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
@ -40,7 +40,7 @@ public class ForNode extends Node {
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
LabelContext.popLoop(subtarget.env, label); 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); CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
int endI = subtarget.size(); int endI = subtarget.size();
@ -51,7 +51,7 @@ public class ForNode extends Node {
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) subtarget.add(Instruction.pushUndefined()); 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) { public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {

View File

@ -12,24 +12,20 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.LabelContext;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.patterns.Binding;
import me.topchetoeu.jscript.compilation.scope.Variable;
import me.topchetoeu.jscript.compilation.values.VariableNode;
public class ForOfNode extends Node { public class ForOfNode extends Node {
public final String varName; public final Binding binding;
public final DeclarationType declType;
public final Node iterable, body; public final Node iterable, body;
public final String label; public final String label;
public final Location varLocation;
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
body.resolve(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) { @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); iterable.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.dup()); target.add(Instruction.dup());
@ -46,8 +42,8 @@ public class ForOfNode extends Node {
target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
int mid = target.temp(); int mid = target.temp();
target.add(Instruction.loadMember("value")).setLocation(varLocation); target.add(Instruction.loadMember("value")).setLocation(binding.loc);
target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict)); binding.assign(target, false);
var end = new DeferredIntSupplier(); var end = new DeferredIntSupplier();
@ -65,12 +61,10 @@ public class ForOfNode extends Node {
if (pollute) target.add(Instruction.pushUndefined()); 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); super(loc);
this.varLocation = varLocation;
this.label = label; this.label = label;
this.declType = declType; this.binding = binding;
this.varName = varName;
this.iterable = object; this.iterable = object;
this.body = body; this.body = body;
} }
@ -91,13 +85,9 @@ public class ForOfNode extends Node {
n++; n++;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
var declType = JavaScript.parseDeclarationType(src, i + n); var binding = Binding.parse(src, i + n);
n += declType.n; if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop");
n += binding.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;
n += Parsing.skipEmpty(src, i + 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"); 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"); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body");
n += bodyRes.n; 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);
} }
} }

View File

@ -44,7 +44,7 @@ public class SwitchNode extends Node {
value.compile(target, true, BreakpointType.STEP_OVER); value.compile(target, true, BreakpointType.STEP_OVER);
var subtarget = target.subtarget(); var subtarget = target.subtarget();
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); subtarget.beginScope();
// TODO: create a jump map // TODO: create a jump map
for (var ccase : cases) { for (var ccase : cases) {
@ -64,7 +64,7 @@ public class SwitchNode extends Node {
} }
LabelContext.getBreak(target.env).pop(label); LabelContext.getBreak(target.env).pop(label);
subtarget.scope.end(); subtarget.endScope();
int endI = subtarget.size(); int endI = subtarget.size();
end.set(endI); end.set(endI);

View File

@ -43,13 +43,14 @@ public class TryNode extends Node {
if (captureName != null) { if (captureName != null) {
var subtarget = target.subtarget(); var subtarget = target.subtarget();
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount())); subtarget.beginScope();
subtarget.scope.singleEntry = true; subtarget.scope.singleEntry = true;
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc()); var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
subtarget.add(Instruction.loadError()); subtarget.add(Instruction.loadError());
subtarget.add(_i -> catchVar.index().toSet(false)); subtarget.add(catchVar.index().toInit());
catchBody.compile(subtarget, false); catchBody.compile(subtarget, false);
subtarget.endScope();
subtarget.scope.end(); subtarget.scope.end();
} }

View File

@ -14,19 +14,17 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
public class AssignPattern implements Pattern { public class AssignPattern implements Pattern {
public final Location loc; public final Location loc;
public final Pattern assignable; public final AssignTarget assignable;
public final Node value; public final Node value;
@Override public Location loc() { return loc; } @Override public Location loc() { return loc; }
@Override public void destructDeclResolve(CompileResult target) { @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) { private void common(CompileResult target) {
throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration");
}
@Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
target.add(Instruction.dup()); target.add(Instruction.dup());
target.add(Instruction.pushUndefined()); target.add(Instruction.pushUndefined());
target.add(Instruction.operation(Operation.EQUALS)); target.add(Instruction.operation(Operation.EQUALS));
@ -36,8 +34,27 @@ public class AssignPattern implements Pattern {
value.compile(target, true); value.compile(target, true);
target.set(start, Instruction.jmpIfNot(target.size() - start)); 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) { public AssignPattern(Location loc, Pattern assignable, Node value) {

View File

@ -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<Binding> 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);
}
}
}

View File

@ -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<AssignTarget> implements AssignTarget {
@Override public void afterAssign(CompileResult target, boolean pollute) {
compile(target, t -> t.assign(target, false), pollute);
}
public ObjectAssignable(Location loc, List<Member<AssignTarget>> members) {
super(loc, members);
}
}

View File

@ -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<T> extends Node {
public static final class Member<T> {
public final Node key;
public final T consumable;
public Member(Node key, T consumer) {
this.key = key;
this.consumable = consumer;
}
}
public final List<Member<T>> members;
public void consume(Consumer<T> consumer) {
for (var el : members) {
consumer.accept(el.consumable);
}
}
public void compile(CompileResult target, Consumer<T> 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<Member<T>> members) {
super(loc);
this.members = members;
}
}

View File

@ -2,36 +2,79 @@ package me.topchetoeu.jscript.compilation.patterns;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; 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.SyntaxException;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.VariableNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode; 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<Member> members;
public void compile(CompileResult target, Consumer<AssignTarget> 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<Pattern> implements Pattern {
@Override public void destructDeclResolve(CompileResult target) { @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) { @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) { @Override public void afterAssign(CompileResult target, boolean pollute) {
throw new SyntaxException(loc(), "Object pattern must be initialized"); compile(target, t -> t.assign(target, false), pollute);
} }
public ObjectPattern(Location loc, List<Member<Pattern>> members) { @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) {
super(loc, members); 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<Member<Pattern>> parseShorthand(Source src, int i) { public ObjectPattern(Location loc, List<Member> members) {
super(loc);
this.members = members;
}
private static ParseRes<Member> parseShorthand(Source src, int i) {
ParseRes<Pattern> res = ParseRes.first(src, i, ParseRes<Pattern> res = ParseRes.first(src, i,
AssignPattern::parse, AssignPattern::parse,
VariableNode::parse VariableNode::parse
@ -40,17 +83,17 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
if (res.isSuccess()) { if (res.isSuccess()) {
if (res.result instanceof AssignPattern assign) { if (res.result instanceof AssignPattern assign) {
if (assign.assignable instanceof VariableNode var) { 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) { 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(); return res.chainError();
} }
private static ParseRes<Member<Pattern>> parseKeyed(Source src, int i) { private static ParseRes<Member> parseKeyed(Source src, int i) {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);
var key = ObjectNode.parsePropName(src, i + n); var key = ObjectNode.parsePropName(src, i + n);
@ -65,7 +108,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon"); if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon");
n += res.n; 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<ObjectPattern> parse(Source src, int i) { public static ParseRes<ObjectPattern> parse(Source src, int i) {
@ -76,7 +119,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
n++; n++;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
var members = new LinkedList<Member<Pattern>>(); var members = new LinkedList<Member>();
if (src.is(i + n, "}")) { if (src.is(i + n, "}")) {
n++; n++;
@ -84,7 +127,7 @@ public class ObjectPattern extends ObjectDestructor<Pattern> implements Pattern
} }
while (true) { while (true) {
ParseRes<Member<Pattern>> prop = ParseRes.first(src, i + n, ParseRes<Member> prop = ParseRes.first(src, i + n,
ObjectPattern::parseKeyed, ObjectPattern::parseKeyed,
ObjectPattern::parseShorthand ObjectPattern::parseShorthand
); );

View File

@ -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) * 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(); Location loc();
/** /**
@ -28,7 +28,21 @@ public interface Pattern {
/** /**
* Run when destructing a declaration without an initializer * 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<Pattern> parse(Source src, int i, boolean withDefault) { public static ParseRes<Pattern> parse(Source src, int i, boolean withDefault) {
return withDefault ? return withDefault ?

View File

@ -36,7 +36,7 @@ public class FunctionScope extends Scope {
} }
else { else {
functionVarMap.put(var.name, var); functionVarMap.put(var.name, var);
return variables.add(var); return locals.add(var);
} }
} }
public Variable defineSpecial(Variable var, Location loc) { 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); if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
specialVarMap.put(var.name, var); specialVarMap.put(var.name, var);
return variables.add(var); return locals.add(var);
} }
@Override public Variable get(String name, boolean capture) { @Override public Variable get(String name, boolean capture) {

View File

@ -10,8 +10,8 @@ import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
public class Scope { public class Scope {
protected final HashMap<String, Variable> strictVarMap = new HashMap<>(); protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset); protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset); protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
private boolean ended = false; private boolean ended = false;
private boolean finished = false; private boolean finished = false;
@ -29,7 +29,7 @@ public class Scope {
protected void transferCaptured(Variable var) { protected void transferCaptured(Variable var) {
if (!singleEntry) { if (!singleEntry) {
this.captured.add(var); this.capturables.add(var);
} }
else if (parent != null) { else if (parent != null) {
parent.transferCaptured(var); parent.transferCaptured(var);
@ -60,7 +60,7 @@ public class Scope {
*/ */
public Variable defineTemp() { public Variable defineTemp() {
checkNotEnded(); checkNotEnded();
return this.variables.add(new Variable("<temp>", false)); return this.locals.add(new Variable("<temp>", false));
} }
/** /**
@ -96,7 +96,7 @@ public class Scope {
if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name); if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name);
strictVarMap.put(var.name, var); 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 * 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; var res = 0;
for (var curr = parent; curr != null; curr = curr.parent) { for (var curr = parent; curr != null; curr = curr.parent) {
res += parent.variables.size(); res += parent.locals.size();
} }
return res; return res;
@ -138,7 +138,7 @@ public class Scope {
var res = 0; var res = 0;
for (var curr = this; curr != null; curr = curr.parent) { 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(); if (curr.parent == null) res += curr.localsCount();
} }
@ -157,19 +157,26 @@ public class Scope {
if (res < childN) res = childN; if (res < childN) res = childN;
} }
return res + variables.size(); return res + locals.size();
} }
public int capturesCount() { return 0; } public int capturesCount() { return 0; }
public int allocCount() { public int allocCount() {
var res = captured.size(); var res = capturables.size();
return res; return res;
} }
public int capturablesCount() { public int capturablesCount() {
var res = captured.size(); var res = capturables.size();
for (var child : children) res += child.capturablesCount(); for (var child : children) res += child.capturablesCount();
return res; return res;
} }
public Iterable<Variable> capturables() {
return capturables.all();
}
public Iterable<Variable> locals() {
return locals.all();
}
/** /**
* Ends this scope. This will make it possible for another child to take its place * 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() { protected void onFinish() {
this.variables.freeze(); this.locals.freeze();
this.captured.freeze(); this.capturables.freeze();
} }
/** /**

View File

@ -30,9 +30,25 @@ public final class VariableIndex {
} }
public final Instruction toSet(boolean keep) { public final Instruction toSet(boolean keep) {
switch (type) { switch (type) {
case CAPTURES: return Instruction.storeVar(~index, keep); case CAPTURES: return Instruction.storeVar(index, keep, false);
case CAPTURABLES: return Instruction.storeVar(index, keep); case CAPTURABLES: return Instruction.storeVar(index, keep, false);
case LOCALS: return Instruction.storeVar(index, keep); 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); default: throw new UnsupportedOperationException("Unknown index type " + type);
} }
} }

View File

@ -19,9 +19,8 @@ import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.Parameters; import me.topchetoeu.jscript.compilation.Parameters;
import me.topchetoeu.jscript.compilation.patterns.AssignTarget; import me.topchetoeu.jscript.compilation.patterns.AssignTarget;
import me.topchetoeu.jscript.compilation.patterns.AssignTargetLike; 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.Pattern;
import me.topchetoeu.jscript.compilation.patterns.ObjectDestructor.Member;
import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode;
import me.topchetoeu.jscript.compilation.values.operations.AssignNode; import me.topchetoeu.jscript.compilation.values.operations.AssignNode;
@ -296,18 +295,18 @@ public class ObjectNode extends Node implements AssignTargetLike {
} }
@Override public AssignTarget toAssignTarget() { @Override public AssignTarget toAssignTarget() {
var newMembers = new LinkedList<Member<AssignTarget>>(); var newMembers = new LinkedList<ObjectPattern.Member>();
for (var el : members) { for (var el : members) {
if (el instanceof FieldMemberNode field) { 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 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"); 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<Node> map) { public ObjectNode(Location loc, List<Node> map) {

View File

@ -26,17 +26,18 @@ public class VariableNode extends Node implements Pattern, ChangeTarget {
} }
@Override public void destructDeclResolve(CompileResult target) { @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) { @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 != null) {
if (decl.strict) target.scope.defineStrict(new Variable(name, decl.readonly), loc()); var i = target.scope.define(decl, name, loc());
else target.scope.define(new Variable(name, decl.readonly), loc()); target.add(_i -> i.index().toUndefinedInit(decl.strict));
} }
else target.add(_i -> { else target.add(_i -> {
var i = target.scope.get(name, false); 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) { @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) {
if (!shouldDeclare || decl == null) { 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 { else {
if (decl == DeclarationType.VAR && target.scope.has(name, false)) throw new SyntaxException(loc(), "Duplicate parameter name not allowed"); 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()); 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) { @Override public void compile(CompileResult target, boolean pollute) {
var i = target.scope.get(name, false); target.add(toGet(target, loc(), name, true, 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());
} }
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, boolean forceGet) { public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) {
var i = target.scope.get(name, false); var oldI = target.scope.get(name, true);
if (i == null) return _i -> { if (oldI != null) {
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); if (keep) return _i -> oldI.index().toGet();
else return Instruction.globGet(name, forceGet); 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<Instruction> toGet(CompileResult target, Location loc, String name) { public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name) {
return toGet(target, loc, name, false); return toGet(target, loc, name, true, false);
} }
public static IntFunction<Instruction> toInit(CompileResult target, Location loc, String name) {
var oldI = target.scope.get(name, true);
public static IntFunction<Instruction> toSet(CompileResult target, Location loc, String name, boolean keep, boolean define) { if (oldI != null) return _i -> oldI.index().toInit();
var i = target.scope.get(name, false); else return _i -> {
var i = target.scope.get(name, false);
if (i == null) return _i -> { if (i == null) return Instruction.globSet(name, false, true);
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name)); else return i.index().toInit();
else return Instruction.globSet(name, keep, define); };
}
public static IntFunction<Instruction> 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) { public VariableNode(Location loc, String name) {

View File

@ -16,7 +16,7 @@ public class TypeofNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (value instanceof VariableNode varNode) { 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()); if (pollute) target.add(Instruction.typeof());
else target.add(Instruction.discard()); else target.add(Instruction.discard());

View File

@ -18,11 +18,11 @@ public class VariableAssignNode extends Node {
target.add(VariableNode.toGet(target, loc(), name)); target.add(VariableNode.toGet(target, loc(), name));
FunctionNode.compileWithName(value, target, true, name); FunctionNode.compileWithName(value, target, true, name);
target.add(Instruction.operation(operation)); target.add(Instruction.operation(operation));
target.add(VariableNode.toSet(target, loc(), name, pollute, false)); target.add(VariableNode.toSet(target, loc(), name, pollute));
} }
else { else {
FunctionNode.compileWithName(value, target, true, name); FunctionNode.compileWithName(value, target, true, name);
target.add(VariableNode.toSet(target, loc(), name, pollute, false)); target.add(VariableNode.toSet(target, loc(), name, pollute));
} }
} }

View File

@ -1,6 +1,5 @@
package me.topchetoeu.jscript.runtime; package me.topchetoeu.jscript.runtime;
import java.util.Arrays;
import java.util.Stack; import java.util.Stack;
import java.util.concurrent.CancellationException; import java.util.concurrent.CancellationException;
@ -361,15 +360,7 @@ public final class Frame {
this.argsVal = new ArgumentsValue(this, args); this.argsVal = new ArgumentsValue(this, args);
this.captures = func.captures; this.captures = func.captures;
var i = 0;
this.locals = new Value[func.body.localsN]; this.locals = new Value[func.body.localsN];
Arrays.fill(locals, Value.UNDEFINED);
this.capturables = new Value[func.body.capturablesN][1]; this.capturables = new Value[func.body.capturablesN][1];
for (i = 0; i < func.body.capturablesN; i++) {
this.capturables[i][0] = Value.UNDEFINED;
}
} }
} }

View File

@ -90,7 +90,7 @@ public class InstructionRunner {
var members = new ArrayList<>(val.getMembers(env, instr.get(0), instr.get(1))); var members = new ArrayList<>(val.getMembers(env, instr.get(0), instr.get(1)));
Collections.reverse(members); Collections.reverse(members);
frame.push(null); frame.push(Value.UNDEFINED);
for (var el : members) { for (var el : members) {
var obj = new ObjectValue(); var obj = new ObjectValue();
@ -147,7 +147,9 @@ public class InstructionRunner {
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
int i = instr.get(0); 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++; frame.codePtr++;
return null; return null;
@ -285,6 +287,7 @@ public class InstructionRunner {
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
int i = instr.get(0); int i = instr.get(0);
if (!(boolean)instr.get(2) && frame.getVar(i) == null) throw EngineException.ofSyntax("Uninitialized variable");
frame.setVar(i, val); frame.setVar(i, val);
frame.codePtr++; frame.codePtr++;
@ -516,21 +519,21 @@ public class InstructionRunner {
return null; return null;
} }
private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) { private static Value execVarInit(Environment env, Instruction instr, Frame frame) {
int offset = instr.get(0); if ((boolean)instr.get(1) || frame.getVar(instr.get(0)) == null) {
int n = instr.get(1); frame.setVar(instr.get(0), Value.UNDEFINED);
}
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { Value.UNDEFINED };
frame.codePtr++; frame.codePtr++;
return null; return null;
} }
private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) { private static Value execVarFree(Environment env, Instruction instr, Frame frame) {
int offset = instr.get(0); frame.locals[(int)instr.get(0)] = null;
int n = instr.get(1); frame.codePtr++;
return null;
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { frame.capturables[i][0] }; }
private static Value execCapFree(Environment env, Instruction instr, Frame frame) {
frame.capturables[(int)instr.get(0) - frame.locals.length] = new Value[1];
frame.codePtr++; frame.codePtr++;
return null; return null;
} }
@ -592,8 +595,9 @@ public class InstructionRunner {
case GLOB_GET: return exexGlobGet(env, instr, frame); case GLOB_GET: return exexGlobGet(env, instr, frame);
case GLOB_SET: return exexGlobSet(env, instr, frame); case GLOB_SET: return exexGlobSet(env, instr, frame);
case STACK_ALLOC: return execStackAlloc(env, instr, frame); case VAR_INIT: return execVarInit(env, instr, frame);
case STACK_REALLOC: return execStackRealloc(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() + "."); default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
} }

View File

@ -1,3 +1,5 @@
return;
const target = arguments[0]; const target = arguments[0];
const primordials = arguments[1]; const primordials = arguments[1];