ES6 Support Groundwork + Fixes #26

Merged
TopchetoEU merged 49 commits from ES6 into master 2024-09-05 14:26:07 +00:00
14 changed files with 331 additions and 133 deletions
Showing only changes of commit 506726fd76 - Show all commits

View File

@ -3,12 +3,11 @@ package me.topchetoeu.jscript.common;
public class FunctionBody {
public final FunctionBody[] children;
public final Instruction[] instructions;
public final int localsN, capturesN, argsN, length;
public final int localsN, capturesN, length;
public FunctionBody(int localsN, int capturesN, int length, int argsN, Instruction[] instructions, FunctionBody[] children) {
public FunctionBody(int localsN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
this.children = children;
this.length = length;
this.argsN = argsN;
this.localsN = localsN;
this.capturesN = capturesN;
this.instructions = instructions;

View File

@ -1,16 +1,15 @@
package me.topchetoeu.jscript.common;
// import java.io.DataInputStream;
// import java.io.DataOutputStream;
// import java.io.IOException;
import java.util.HashMap;
import java.util.function.IntFunction;
import java.util.function.IntSupplier;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class Instruction {
public static enum Type {
NOP(0x00),
RETURN(0x01),
RETURN(0x00),
NOP(0x01),
THROW(0x02),
THROW_SYNTAX(0x03),
DELETE(0x04),
@ -41,10 +40,18 @@ public class Instruction {
LOAD_VAR(0x40),
LOAD_MEMBER(0x41),
LOAD_ARGS(0x42),
LOAD_THIS(0x43),
LOAD_MEMBER_INT(0x42),
LOAD_MEMBER_STR(0x43),
LOAD_ARGS(0x44),
LOAD_REST_ARGS(0x45),
LOAD_CALLEE(0x46),
LOAD_THIS(0x47),
STORE_VAR(0x48),
STORE_MEMBER(0x49),
STORE_MEMBER_INT(0x4A),
STORE_MEMBER_STR(0x4B),
DEF_PROP(0x50),
KEYS(0x51),
@ -286,6 +293,7 @@ public class Instruction {
public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, argn, "");
}
public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset);
}
@ -296,6 +304,17 @@ public class Instruction {
return new Instruction(Type.JMP_IFN, offset);
}
public static IntFunction<Instruction> jmp(IntSupplier pos) {
return i -> new Instruction(Type.JMP, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIf(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IF, pos.getAsInt() - i);
}
public static IntFunction<Instruction> jmpIfNot(IntSupplier pos) {
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
}
public static Instruction pushUndefined() {
return new Instruction(Type.PUSH_UNDEFINED);
}
@ -313,7 +332,7 @@ public class Instruction {
}
public static Instruction globDef(String name) {
return new Instruction(Type.GLOB_GET, name);
return new Instruction(Type.GLOB_DEF, name);
}
public static Instruction globGet(String name) {
@ -332,6 +351,12 @@ public class Instruction {
public static Instruction loadArgs() {
return new Instruction(Type.LOAD_ARGS);
}
public static Instruction loadRestArgs(int offset) {
return new Instruction(Type.LOAD_REST_ARGS, offset);
}
public static Instruction loadCallee() {
return new Instruction(Type.LOAD_CALLEE);
}
public static Instruction loadGlob() {
return new Instruction(Type.LOAD_GLOB);
}
@ -341,17 +366,26 @@ public class Instruction {
public static Instruction loadMember() {
return new Instruction(Type.LOAD_MEMBER);
}
public static Instruction loadMember(int member) {
return new Instruction(Type.LOAD_MEMBER_INT, member);
}
public static Instruction loadMember(String member) {
return new Instruction(Type.LOAD_MEMBER_STR, member);
}
public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, flags);
}
public static Instruction loadFunc(int id, String name, int[] captures) {
public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, String name, int[] captures) {
if (name == null) name = "";
var args = new Object[2 + captures.length];
var args = new Object[5 + captures.length];
args[0] = id;
args[1] = name;
for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i];
args[2] = callable;
args[3] = constructible;
args[4] = captureThis;
for (var i = 0; i < captures.length; i++) args[i + 5] = captures[i];
return new Instruction(Type.LOAD_FUNC, args);
}
public static Instruction loadObj() {
@ -373,12 +407,28 @@ public class Instruction {
public static Instruction storeVar(int i, boolean keep) {
return new Instruction(Type.STORE_VAR, i, keep);
}
public static Instruction storeMember() {
return new Instruction(Type.STORE_MEMBER, false);
}
public static Instruction storeMember(boolean keep) {
return new Instruction(Type.STORE_MEMBER, keep);
}
public static Instruction storeMember(String key) {
return new Instruction(Type.STORE_MEMBER_STR, key, false);
}
public static Instruction storeMember(String key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
}
public static Instruction storeMember(int key) {
return new Instruction(Type.STORE_MEMBER_INT, key, false);
}
public static Instruction storeMember(int key, boolean keep) {
return new Instruction(Type.STORE_MEMBER_STR, key, keep);
}
public static Instruction discard() {
return new Instruction(Type.DISCARD);
}

View File

@ -22,7 +22,7 @@ public class CompoundNode extends Node {
public void compile(CompileResult target, boolean pollute, boolean alloc, BreakpointType type) {
List<Node> statements = new ArrayList<Node>();
var subtarget = target.subtarget();
var subtarget = alloc ? target.subtarget() : target;
if (alloc) subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
for (var stm : this.statements) {
@ -41,8 +41,10 @@ public class CompoundNode extends Node {
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
}
subtarget.scope.end();
if (alloc) subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
if (alloc) {
subtarget.scope.end();
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
}
if (!polluted && pollute) {
target.add(Instruction.pushUndefined());

View File

@ -0,0 +1,73 @@
package me.topchetoeu.jscript.compilation;
import java.util.List;
import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
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.control.ReturnNode;
public class FunctionArrowNode extends FunctionNode {
@Override public String name() { return null; }
@Override protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
target.add(Instruction.loadFunc(id, true, false, true, null, captures));
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
compile(target, pollute, false, name, null, bp);
}
public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {
super(loc, end, params, expToBody(body));
}
private static final CompoundNode expToBody(Node node) {
if (node instanceof CompoundNode res) return res;
else return new CompoundNode(node.loc(), new ReturnNode(node.loc(), node));
}
public static ParseRes<FunctionArrowNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
Parameters params;
if (src.is(i + n, "(")) {
var paramsRes = JavaScript.parseParameters(src, i + n);
if (!paramsRes.isSuccess()) return paramsRes.chainError();
n += paramsRes.n;
n += Parsing.skipEmpty(src, i + n);
params = paramsRes.result;
}
else {
var singleParam = Parsing.parseIdentifier(src, i + n);
if (!singleParam.isSuccess()) return ParseRes.failed();
var paramLoc = src.loc(i + n);
n += singleParam.n;
n += Parsing.skipEmpty(src, i + n);
params = new Parameters(List.of(new Parameter(paramLoc, singleParam.result, null)));
}
if (!src.is(i + n, "=>")) return ParseRes.failed();
n += 2;
ParseRes<Node> body = ParseRes.first(src, i + n,
(s, j) -> JavaScript.parseExpression(s, j, 2),
CompoundNode::parse
);
if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected an expression or a compount statement after '=>'");
n += body.n;
return ParseRes.res(new FunctionArrowNode(
loc, src.loc(i + n - 1),
params, body.result
), n);
}
}

View File

@ -35,11 +35,11 @@ public abstract class FunctionNode extends Node {
// }
// }
protected void compileLoadFunc(CompileResult target, int[] captures, String name) {
target.add(Instruction.loadFunc(target.children.size(), name, captures));
protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
target.add(Instruction.loadFunc(id, true, true, false, name, captures));
}
private CompileResult compileBody(CompileResult target, String name, boolean storeSelf, boolean pollute, BreakpointType bp) {
private CompileResult compileBody(CompileResult target, boolean hasArgs, String name, String selfName, boolean pollute, BreakpointType bp) {
var env = target.env.child()
.remove(LabelContext.BREAK_CTX)
.remove(LabelContext.CONTINUE_CTX);
@ -47,43 +47,74 @@ public abstract class FunctionNode extends Node {
var funcScope = new FunctionScope(target.scope);
var subtarget = new CompileResult(env, new LocalScope(funcScope));
for (var param : params.params) {
// TODO: Implement default values
// TODO: Implement argument location
if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
var i = funcScope.defineParam(param.name, param.loc);
subtarget.length = params.params.size();
if (param.node != null) {
var end = new DeferredIntSupplier();
if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs());
subtarget.add(_i -> Instruction.loadVar(i.index()));
subtarget.add(Instruction.pushUndefined());
subtarget.add(Instruction.operation(Operation.EQUALS));
subtarget.add(_i -> Instruction.jmpIfNot(end.getAsInt() - _i));
param.node.compile(subtarget, pollute);
subtarget.add(_i -> Instruction.storeVar(i.index()));
if (hasArgs) {
var argsVar = funcScope.defineParam("arguments", true, loc());
subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
}
end.set(subtarget.size());
if (params.params.size() > 0) {
if (params.params.size() > 1) subtarget.add(Instruction.dup(params.params.size() - 1));
var i = 0;
for (var param : params.params) {
if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
if (!JavaScript.checkVarName(param.name)) {
throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name));
}
var varI = funcScope.defineParam(param.name, false, param.loc);
subtarget.add(Instruction.loadMember(i++));
if (param.node != null) {
var end = new DeferredIntSupplier();
subtarget.add(Instruction.dup());
subtarget.add(Instruction.pushUndefined());
subtarget.add(Instruction.operation(Operation.EQUALS));
subtarget.add(Instruction.jmpIfNot(end));
subtarget.add(Instruction.discard());
param.node.compile(subtarget, pollute);
end.set(subtarget.size());
}
subtarget.add(Instruction.storeVar(varI.index()));
}
}
if (params.restName != null) {
if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
var restVar = funcScope.defineParam(params.restName, true, params.restLocation);
subtarget.add(Instruction.loadRestArgs(params.params.size()));
subtarget.add(_i -> Instruction.storeVar(restVar.index()));
}
if (selfName != null && !funcScope.hasArg(name)) {
var i = funcScope.defineParam(selfName, true, end);
subtarget.add(Instruction.loadCallee());
subtarget.add(_i -> Instruction.storeVar(i.index(), false));
}
body.resolve(subtarget);
body.compile(subtarget, false, false, BreakpointType.NONE);
subtarget.length = params.length;
subtarget.assignN = params.params.size();
subtarget.scope.end();
funcScope.end();
if (pollute) compileLoadFunc(target, funcScope.getCaptureIndices(), name);
if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name);
return target.addChild(subtarget);
}
public void compile(CompileResult target, boolean pollute, boolean storeSelf, String name, BreakpointType bp) {
public void compile(CompileResult target, boolean pollute, boolean hasArgs, String name, String selfName, BreakpointType bp) {
if (this.name() != null) name = this.name();
compileBody(target, name, storeSelf, pollute, bp);
compileBody(target, hasArgs, name, selfName, pollute, bp);
}
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
public void compile(CompileResult target, boolean pollute, String name) {

View File

@ -14,7 +14,7 @@ public class FunctionStatementNode extends FunctionNode {
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
compile(target, true, false, this.name, bp);
compile(target, true, true, name, this.name, bp);
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
}

View File

@ -9,7 +9,7 @@ public class FunctionValueNode extends FunctionNode {
@Override public String name() { return name; }
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
compile(target, pollute, true, name, bp);
compile(target, pollute, true, name, null, bp);
}
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {

View File

@ -26,9 +26,7 @@ import me.topchetoeu.jscript.compilation.control.TryNode;
import me.topchetoeu.jscript.compilation.control.WhileNode;
import me.topchetoeu.jscript.compilation.scope.GlobalScope;
import me.topchetoeu.jscript.compilation.scope.LocalScope;
import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
import me.topchetoeu.jscript.compilation.values.ArrayNode;
import me.topchetoeu.jscript.compilation.values.GlobalThisNode;
import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.RegexNode;
import me.topchetoeu.jscript.compilation.values.ThisNode;
@ -45,7 +43,20 @@ import me.topchetoeu.jscript.compilation.values.operations.OperationNode;
import me.topchetoeu.jscript.compilation.values.operations.TypeofNode;
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
public class JavaScript {
public final class JavaScript {
public static enum DeclarationType {
VAR(false, false),
CONST(true, true),
LET(true, false);
public final boolean strict, readonly;
private DeclarationType(boolean strict, boolean readonly) {
this.strict = strict;
this.readonly = readonly;
}
}
static final Set<String> reserved = Set.of(
"true", "false", "void", "null", "this", "if", "else", "try", "catch",
"finally", "for", "do", "while", "switch", "case", "default", "new",
@ -84,6 +95,7 @@ public class JavaScript {
ChangeNode::parsePrefixIncrease,
OperationNode::parsePrefix,
ArrayNode::parse,
FunctionArrowNode::parse,
JavaScript::parseParens,
CallNode::parseNew,
TypeofNode::parse,
@ -103,11 +115,11 @@ public class JavaScript {
if (id.result.equals("true")) return ParseRes.res(new BoolNode(loc, true), n);
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n);
// if (id.result.equals("undefined")) return ParseRes.res(new DiscardNode(loc, null), n);
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n);
if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n);
if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
// if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n);
// if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
return ParseRes.failed();
}
@ -228,10 +240,25 @@ public class JavaScript {
while (true) {
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "...")) {
n += 3;
var restLoc = src.loc(i);
var restName = Parsing.parseIdentifier(src, i + n);
if (!restName.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter");
n += restName.n;
n += Parsing.skipEmpty(src, i + n);
if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected an end of parameters list after rest parameter");
n++;
return ParseRes.res(new Parameters(params, restName.result, restLoc), n);
}
var paramLoc = src.loc(i);
var name = Parsing.parseIdentifier(src, i + n);
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an argument or a closing brace");
if (!name.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace");
n += name.n;
n += Parsing.skipEmpty(src, i + n);
@ -239,7 +266,7 @@ public class JavaScript {
n++;
var val = parseExpression(src, i + n, 2);
if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a default value");
if (!val.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter default value");
n += val.n;
n += Parsing.skipEmpty(src, i + n);
@ -251,6 +278,7 @@ public class JavaScript {
n++;
n += Parsing.skipEmpty(src, i + n);
}
if (src.is(i + n, ")")) {
n++;
break;
@ -261,6 +289,17 @@ public class JavaScript {
return ParseRes.res(new Parameters(params), n);
}
public static ParseRes<DeclarationType> parseDeclarationType(Source src, int i) {
var res = Parsing.parseIdentifier(src, i);
if (!res.isSuccess()) return res.chainError();
if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, res.n);
if (res.result.equals("let")) return ParseRes.res(DeclarationType.LET, res.n);
if (res.result.equals("const")) return ParseRes.res(DeclarationType.CONST, res.n);
return ParseRes.failed();
}
public static Node[] parse(Environment env, Filename filename, String raw) {
var src = new Source(env, filename, raw);
var list = new ArrayList<Node>();
@ -289,17 +328,20 @@ public class JavaScript {
public static CompileResult compile(Environment env, Node ...statements) {
var target = new CompileResult(env, new LocalScope(new GlobalScope()));
var stm = new CompoundNode(null, statements);
var argsI = target.scope.defineStrict("arguments", true, null);
target.add(Instruction.loadArgs());
target.add(_i -> Instruction.storeVar(argsI.index()));
try {
// try {
stm.resolve(target);
stm.compile(target, true, false, BreakpointType.NONE);
// FunctionNode.checkBreakAndCont(target, 0);
}
catch (SyntaxException e) {
target = new CompileResult(env, new LocalScope(new GlobalScope()));
// }
// catch (SyntaxException e) {
// target = new CompileResult(env, new LocalScope(new GlobalScope()));
target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
}
// target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
// }
return target;
}

View File

@ -1,16 +1,16 @@
package me.topchetoeu.jscript.compilation;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import me.topchetoeu.jscript.common.parsing.Location;
public final class Parameters {
public final int length;
public final List<Parameter> params;
public final Set<String> names;
public final String restName;
public final Location restLocation;
public Parameters(List<Parameter> params) {
this.names = new HashSet<>();
public Parameters(List<Parameter> params, String restName, Location restLocation) {
var len = params.size();
for (var i = params.size() - 1; i >= 0; i--) {
@ -18,11 +18,12 @@ public final class Parameters {
len--;
}
for (var param : params) {
this.names.add(param.name);
}
this.params = params;
this.length = len;
this.restName = restName;
this.restLocation = restLocation;
}
public Parameters(List<Parameter> params) {
this(params, null, null);
}
}

View File

@ -9,6 +9,7 @@ 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.JavaScript.DeclarationType;
import me.topchetoeu.jscript.compilation.values.VariableNode;
public class VariableDeclareNode extends Node {
@ -25,51 +26,56 @@ public class VariableDeclareNode extends Node {
}
public final List<Pair> values;
public final DeclarationType declType;
@Override public void resolve(CompileResult target) {
for (var entry : values) {
target.scope.define(entry.name, false, entry.location);
if (!declType.strict) {
for (var entry : values) {
target.scope.define(entry.name, false, entry.location);
}
}
}
// let a = 10, b = "test"; var c = () => a + b
@Override public void compile(CompileResult target, boolean pollute) {
for (var entry : values) {
if (entry.name == null) continue;
if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location);
if (entry.value != null) {
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
target.add(VariableNode.toSet(target, entry.location, entry.name, false, true));
}
else {
target.add(_i -> {
var i = target.scope.get(entry.name, true);
else target.add(_i -> {
var i = target.scope.get(entry.name, true);
if (i == null) return Instruction.globDef(entry.name);
else return Instruction.nop();
});
}
if (i == null) return Instruction.globDef(entry.name);
else return Instruction.nop();
});
}
if (pollute) target.add(Instruction.pushUndefined());
}
public VariableDeclareNode(Location loc, List<Pair> values) {
public VariableDeclareNode(Location loc, DeclarationType declType, List<Pair> values) {
super(loc);
this.values = values;
this.declType = declType;
}
public static ParseRes<VariableDeclareNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n);
if (!Parsing.isIdentifier(src, i + n, "var")) return ParseRes.failed();
n += 3;
var declType = JavaScript.parseDeclarationType(src, i + n);
if (!declType.isSuccess()) return declType.chainError();
n += declType.n;
var res = new ArrayList<Pair>();
var end = JavaScript.parseStatementEnd(src, i + n);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new VariableDeclareNode(loc, res), n);
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
}
while (true) {
@ -83,6 +89,7 @@ public class VariableDeclareNode extends Node {
}
Node val = null;
var endN = n;
n += Parsing.skipEmpty(src, i + n);
if (src.is(i + n, "=")) {
@ -92,6 +99,7 @@ public class VariableDeclareNode extends Node {
if (!valRes.isSuccess()) return valRes.chainError(src.loc(i + n), "Expected a value after '='");
n += valRes.n;
endN = n;
n += Parsing.skipEmpty(src, i + n);
val = valRes.result;
}
@ -103,11 +111,11 @@ public class VariableDeclareNode extends Node {
continue;
}
end = JavaScript.parseStatementEnd(src, i + n);
end = JavaScript.parseStatementEnd(src, i + endN);
if (end.isSuccess()) {
n += end.n;
return ParseRes.res(new VariableDeclareNode(loc, res), n);
n += end.n + endN - n;
return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n);
}
else return end.chainError(src.loc(i + n), "Expected a comma or end of statement");
}

View File

@ -12,21 +12,24 @@ 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.values.VariableNode;
public class ForInNode extends Node {
public final String varName;
public final boolean isDeclaration;
public final DeclarationType declType;
public final Node object, body;
public final String label;
public final Location varLocation;
@Override public void resolve(CompileResult target) {
body.resolve(target);
if (isDeclaration) target.scope.define(varName, false, loc());
if (declType != null && !declType.strict) target.scope.define(varName, false, loc());
}
@Override public void compile(CompileResult target, boolean pollute) {
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
object.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.keys(true));
@ -36,9 +39,8 @@ public class ForInNode extends Node {
target.add(Instruction.operation(Operation.EQUALS));
int mid = target.temp();
target.add(Instruction.pushValue("value")).setLocation(varLocation);
target.add(Instruction.loadMember()).setLocation(varLocation);
target.add(VariableNode.toSet(target, loc(), varName, pollute, isDeclaration));
target.add(Instruction.loadMember("value")).setLocation(varLocation);
target.add(VariableNode.toSet(target, loc(), varName, pollute, declType != null && declType.strict));
target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER);
var end = new DeferredIntSupplier();
@ -49,19 +51,17 @@ public class ForInNode extends Node {
int endI = target.size();
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.set(mid, Instruction.jmpIf(endI - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
}
public ForInNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) {
public ForInNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
super(loc);
this.varLocation = varLocation;
this.label = label;
this.isDeclaration = isDecl;
this.declType = declType;
this.varName = varName;
this.object = object;
this.body = body;
@ -83,12 +83,8 @@ public class ForInNode extends Node {
n++;
n += Parsing.skipEmpty(src, i + n);
var isDecl = false;
if (Parsing.isIdentifier(src, i + n, "var")) {
isDecl = true;
n += 3;
}
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");
@ -111,6 +107,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, isDecl, name.result, obj.result, bodyRes.result), n);
return ParseRes.res(new ForInNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
}
}

View File

@ -23,28 +23,31 @@ public class ForNode extends Node {
body.resolve(target);
}
@Override public void compile(CompileResult target, boolean pollute) {
declaration.compile(target, false, BreakpointType.STEP_OVER);
var subtarget = target.subtarget();
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
int start = target.size();
condition.compile(target, true, BreakpointType.STEP_OVER);
int mid = target.temp();
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
int start = subtarget.size();
condition.compile(subtarget, true, BreakpointType.STEP_OVER);
int mid = subtarget.temp();
var end = new DeferredIntSupplier();
LabelContext.pushLoop(target.env, loc(), label, end, start);
body.compile(target, false, BreakpointType.STEP_OVER);
LabelContext.popLoop(target.env, label);
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
body.compile(subtarget, false, BreakpointType.STEP_OVER);
LabelContext.popLoop(subtarget.env, label);
// int beforeAssign = target.size();
assignment.compile(target, false, BreakpointType.STEP_OVER);
int endI = target.size();
assignment.compile(subtarget, false, BreakpointType.STEP_OVER);
int endI = subtarget.size();
end.set(endI);
// WhileNode.replaceBreaks(target, label, mid + 1, end, beforeAssign, end + 1);
subtarget.add(Instruction.jmp(start - endI));
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) subtarget.add(Instruction.pushUndefined());
target.add(Instruction.jmp(start - endI));
target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) target.add(Instruction.pushUndefined());
subtarget.scope.end();
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
}
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {

View File

@ -11,42 +11,41 @@ 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.values.VariableNode;
public class ForOfNode extends Node {
public final String varName;
public final boolean isDeclaration;
public final DeclarationType declType;
public final Node iterable, body;
public final String label;
public final Location varLocation;
@Override public void resolve(CompileResult target) {
body.resolve(target);
if (isDeclaration) target.scope.define(varName, false, varLocation);
if (declType != null && !declType.strict) target.scope.define(varName, false, varLocation);
}
@Override public void compile(CompileResult target, boolean pollute) {
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
iterable.compile(target, true, BreakpointType.STEP_OVER);
target.add(Instruction.dup());
target.add(Instruction.loadIntrinsics("it_key"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.call(0)).setLocation(iterable.loc());
int start = target.size();
target.add(Instruction.dup());
target.add(Instruction.dup());
target.add(Instruction.pushValue("next"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.loadMember("next")).setLocation(iterable.loc());
target.add(Instruction.call(0)).setLocation(iterable.loc());
target.add(Instruction.dup());
target.add(Instruction.pushValue("done"));
target.add(Instruction.loadMember()).setLocation(iterable.loc());
target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
int mid = target.temp();
target.add(Instruction.pushValue("value"));
target.add(Instruction.loadMember()).setLocation(varLocation);
target.add(VariableNode.toSet(target, varLocation, varName, false, isDeclaration));
target.add(Instruction.loadMember("value")).setLocation(varLocation);
target.add(VariableNode.toSet(target, varLocation, varName, false, declType != null && declType.strict));
var end = new DeferredIntSupplier();
@ -57,8 +56,6 @@ public class ForOfNode extends Node {
int endI = target.size();
end.set(endI);
// WhileNode.replaceBreaks(target, label, mid + 1, end, start, end + 1);
target.add(Instruction.jmp(start - endI));
target.add(Instruction.discard());
target.add(Instruction.discard());
@ -66,11 +63,11 @@ public class ForOfNode extends Node {
if (pollute) target.add(Instruction.pushUndefined());
}
public ForOfNode(Location loc, Location varLocation, String label, boolean isDecl, String varName, Node object, Node body) {
public ForOfNode(Location loc, Location varLocation, String label, DeclarationType declType, String varName, Node object, Node body) {
super(loc);
this.varLocation = varLocation;
this.label = label;
this.isDeclaration = isDecl;
this.declType = declType;
this.varName = varName;
this.iterable = object;
this.body = body;
@ -92,12 +89,8 @@ public class ForOfNode extends Node {
n++;
n += Parsing.skipEmpty(src, i + n);
var isDecl = false;
if (Parsing.isIdentifier(src, i + n, "var")) {
isDecl = true;
n += 3;
}
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");
@ -120,6 +113,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, isDecl, name.result, obj.result, bodyRes.result), n);
return ParseRes.res(new ForOfNode(loc, nameLoc, label.result, declType.result, name.result, obj.result, bodyRes.result), n);
}
}

View File

@ -42,7 +42,7 @@ public class VariableNode extends Node implements AssignableNode {
if (!pollute) target.add(Instruction.discard());
}
else if (pollute) {
target.add(Instruction.loadVar(i.index()));
target.add(_i -> Instruction.loadVar(i.index()));
}
}
@ -67,8 +67,8 @@ public class VariableNode extends Node implements AssignableNode {
if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
else return Instruction.globSet(name, keep, define);
};
else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable"));
else return _i -> Instruction.storeVar(i.index(), keep);
}
public VariableNode(Location loc, String name) {