diff --git a/src/me/topchetoeu/jscript/compilation/FunctionBody.java b/src/me/topchetoeu/jscript/compilation/FunctionBody.java index 718eeec..7f78066 100644 --- a/src/me/topchetoeu/jscript/compilation/FunctionBody.java +++ b/src/me/topchetoeu/jscript/compilation/FunctionBody.java @@ -3,13 +3,25 @@ package me.topchetoeu.jscript.compilation; public class FunctionBody { public final Instruction[] instructions; public final String[] captureNames, localNames; + public final int localsN, argsN; - public FunctionBody(Instruction[] instructions, String[] captureNames, String[] localNames) { + public FunctionBody(int localsN, int argsN, Instruction[] instructions, String[] captureNames, String[] localNames) { + this.argsN = argsN; + this.localsN = localsN; this.instructions = instructions; this.captureNames = captureNames; this.localNames = localNames; } - public FunctionBody(Instruction[] instructions) { + public FunctionBody(int localsN, int argsN, Instruction[] instructions) { + this.argsN = argsN; + this.localsN = localsN; + this.instructions = instructions; + this.captureNames = new String[0]; + this.localNames = new String[0]; + } + public FunctionBody(Instruction... instructions) { + this.argsN = 0; + this.localsN = 2; this.instructions = instructions; this.captureNames = new String[0]; this.localNames = new String[0]; diff --git a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java index 52c292a..e5f56c8 100644 --- a/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java +++ b/src/me/topchetoeu/jscript/compilation/values/FunctionStatement.java @@ -14,17 +14,18 @@ import me.topchetoeu.jscript.exceptions.SyntaxException; public class FunctionStatement extends Statement { public final CompoundStatement body; - public final String name; + public final String varName; public final String[] args; + public final boolean statement; private static Random rand = new Random(); @Override - public boolean pure() { return name == null; } + public boolean pure() { return varName == null; } @Override public void declare(ScopeRecord scope) { - if (name != null) scope.define(name); + if (varName != null) scope.define(varName); } public static void checkBreakAndCont(CompileTarget target, int start) { @@ -40,73 +41,83 @@ public class FunctionStatement extends Statement { } } - public void compile(CompileTarget target, ScopeRecord scope, String name, boolean isStatement) { + protected long compileBody(CompileTarget target, ScopeRecord scope, boolean polute) { for (var i = 0; i < args.length; i++) { for (var j = 0; j < i; j++) { - if (args[i].equals(args[j])){ - target.add(Instruction.throwSyntax(new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."))); - return; + if (args[i].equals(args[j])) { + throw new SyntaxException(loc(), "Duplicate parameter '" + args[i] + "'."); } } } - var subscope = scope.child(); - int start = target.size(); - var funcTarget = new CompileTarget(target.functions, target.breakpoints); + var id = rand.nextLong(); + var subscope = scope.child(); + var subtarget = new CompileTarget(target.functions, target.breakpoints); subscope.define("this"); var argsVar = subscope.define("arguments"); if (args.length > 0) { for (var i = 0; i < args.length; i++) { - funcTarget.add(Instruction.loadVar(argsVar).locate(loc())); - funcTarget.add(Instruction.loadMember(i).locate(loc())); - funcTarget.add(Instruction.storeVar(subscope.define(args[i])).locate(loc())); + subtarget.add(Instruction.loadVar(loc(), argsVar)); + subtarget.add(Instruction.loadMember(loc(), i)); + subtarget.add(Instruction.storeVar(loc(), subscope.define(args[i]))); } } - if (!isStatement && this.name != null) { - funcTarget.add(Instruction.storeSelfFunc((int)subscope.define(this.name))); + if (!statement && this.varName != null) { + subtarget.add(Instruction.storeSelfFunc(loc(), (int)subscope.define(this.varName))); } body.declare(subscope); - body.compile(funcTarget, subscope, false); - funcTarget.add(Instruction.ret().locate(loc())); - checkBreakAndCont(funcTarget, start); + body.compile(subtarget, subscope, false); + subtarget.add(Instruction.ret(subtarget.lastLoc(loc()))); + checkBreakAndCont(subtarget, 0); - var id = rand.nextLong(); + if (polute) target.add(Instruction.loadFunc(loc(), id, subscope.getCaptures())); + target.functions.put(id, new FunctionBody(subscope.localsCount(), args.length, subtarget.array(), subscope.captures(), subscope.locals())); - target.add(Instruction.loadFunc(id, subscope.localsCount(), args.length, subscope.getCaptures()).locate(loc())); - target.functions.put(id, new FunctionBody(funcTarget.array(), subscope.captures(), subscope.locals())); - - if (name == null) name = this.name; - - if (name != null) { - target.add(Instruction.dup().locate(loc())); - target.add(Instruction.loadValue("name").locate(loc())); - target.add(Instruction.loadValue(name).locate(loc())); - target.add(Instruction.storeMember().locate(loc())); - } - - if (this.name != null && isStatement) { - var key = scope.getKey(this.name); - - if (key instanceof String) target.add(Instruction.makeVar((String)key).locate(loc())); - target.add(Instruction.storeVar(scope.getKey(this.name), false).locate(loc())); - } - - } - @Override - public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { - compile(target, scope, null, false); - if (!pollute) target.add(Instruction.discard().locate(loc())); + return id; } - public FunctionStatement(Location loc, String name, String[] args, CompoundStatement body) { + public void compile(CompileTarget target, ScopeRecord scope, boolean pollute, String name) { + if (this.varName != null) name = this.varName; + + var hasVar = this.varName != null && statement; + var hasName = name != null; + + compileBody(target, scope, pollute || hasVar || hasName); + + if (hasName) { + if (pollute || hasVar) target.add(Instruction.dup(loc())); + target.add(Instruction.loadValue(loc(), "name")); + target.add(Instruction.loadValue(loc(), name)); + target.add(Instruction.storeMember(loc())); + } + + if (hasVar) { + var key = scope.getKey(this.varName); + + if (key instanceof String) target.add(Instruction.makeVar(loc(), (String)key)); + target.add(Instruction.storeVar(loc(), scope.getKey(this.varName), false)); + } + } + @Override public void compile(CompileTarget target, ScopeRecord scope, boolean pollute) { + compile(target, scope, pollute, null); + } + + public FunctionStatement(Location loc, String varName, String[] args, boolean statement, CompoundStatement body) { super(loc); - this.name = name; + + this.varName = varName; + this.statement = statement; this.args = args; this.body = body; } + + public static void compileWithName(Statement stm, CompileTarget target, ScopeRecord scope, boolean pollute, String name) { + if (stm instanceof FunctionStatement) ((FunctionStatement)stm).compile(target, scope, pollute, name); + else stm.compile(target, scope, pollute); + } } diff --git a/src/me/topchetoeu/jscript/engine/frame/Runners.java b/src/me/topchetoeu/jscript/engine/frame/Runners.java index fc76a4e..be1e5d5 100644 --- a/src/me/topchetoeu/jscript/engine/frame/Runners.java +++ b/src/me/topchetoeu/jscript/engine/frame/Runners.java @@ -179,16 +179,13 @@ public class Runners { } public static Object execLoadFunc(Context ctx, Instruction instr, CodeFrame frame) { long id = (Long)instr.get(0); - int localsN = (Integer)instr.get(1); - int len = (Integer)instr.get(2); - var captures = new ValueVariable[instr.params.length - 3]; + var captures = new ValueVariable[instr.params.length - 1]; - for (var i = 3; i < instr.params.length; i++) { - captures[i - 3] = frame.scope.get(instr.get(i)); + for (var i = 1; i < instr.params.length; i++) { + captures[i - 1] = frame.scope.get(instr.get(i)); } - var body = Engine.functions.get(id); - var func = new CodeFunction(ctx.environment(), "", localsN, len, captures, body); + var func = new CodeFunction(ctx.environment(), "", Engine.functions.get(id), captures); frame.push(ctx, func); @@ -306,7 +303,6 @@ public class Runners { var val = frame.pop(); if (!Values.deleteMember(ctx, val, key)) throw EngineException.ofSyntax("Can't delete member '" + key + "'."); - frame.push(ctx, true); frame.codePtr++; return NO_RETURN; } diff --git a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java index adc14d4..313dbc3 100644 --- a/src/me/topchetoeu/jscript/engine/values/CodeFunction.java +++ b/src/me/topchetoeu/jscript/engine/values/CodeFunction.java @@ -11,7 +11,6 @@ import me.topchetoeu.jscript.engine.scope.ValueVariable; public class CodeFunction extends FunctionValue { public final int localsN; - public final int length; public final Instruction[] body; public final String[] captureNames, localNames; public final ValueVariable[] captures; @@ -46,14 +45,13 @@ public class CodeFunction extends FunctionValue { } } - public CodeFunction(Environment environment, String name, int localsN, int length, ValueVariable[] captures, FunctionBody body) { - super(name, length); + public CodeFunction(Environment environment, String name, FunctionBody body, ValueVariable... captures) { + super(name, body.argsN); this.captures = captures; this.captureNames = body.captureNames; this.localNames = body.localNames; this.environment = environment; - this.localsN = localsN; - this.length = length; + this.localsN = body.localsN; this.body = body.instructions; } } diff --git a/src/me/topchetoeu/jscript/parsing/Parsing.java b/src/me/topchetoeu/jscript/parsing/Parsing.java index 95e030f..67c3d9a 100644 --- a/src/me/topchetoeu/jscript/parsing/Parsing.java +++ b/src/me/topchetoeu/jscript/parsing/Parsing.java @@ -808,7 +808,7 @@ public class Parsing { return ParseRes.res(new ObjProp( name, access, - new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), res.result) + new FunctionStatement(loc, access + " " + name.toString(), argsRes.result.toArray(String[]::new), false, res.result) ), n); } public static ParseRes parseObject(Filename filename, List tokens, int i) { @@ -966,7 +966,7 @@ public class Parsing { var res = parseCompound(filename, tokens, i + n); n += res.n; - if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, name, args.toArray(String[]::new), res.result), n); + if (res.isSuccess()) return ParseRes.res(new FunctionStatement(loc, name, args.toArray(String[]::new), statement, res.result), n); else return ParseRes.error(loc, "Expected a compound statement for function.", res); } @@ -1891,19 +1891,19 @@ public class Parsing { } catch (SyntaxException e) { res.target.clear(); - res.add(Instruction.throwSyntax(e)); + res.add(Instruction.throwSyntax(e.loc, e)); } - res.add(Instruction.ret()); + res.add(Instruction.ret(body.loc())); - return new CodeFunction(environment, "", subscope.localsCount(), 0, new ValueVariable[0], new FunctionBody(res.array(), subscope.captures(), subscope.locals())); + return new CodeFunction(environment, "", new FunctionBody(subscope.localsCount(), 0, res.array(), subscope.captures(), subscope.locals()), new ValueVariable[0]); } public static CodeFunction compile(HashMap funcs, TreeSet breakpoints, Environment environment, Filename filename, String raw) { try { return compile(funcs, breakpoints, environment, parse(filename, raw)); } catch (SyntaxException e) { - return new CodeFunction(environment, null, 2, 0, new ValueVariable[0], new FunctionBody(new Instruction[] { Instruction.throwSyntax(e).locate(e.loc) })); + return new CodeFunction(environment, null, new FunctionBody(Instruction.throwSyntax(e.loc, e))); } } }