Add support for source mappings #10
@ -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];
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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<ObjectStatement> parseObject(Filename filename, List<Token> 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<Long, FunctionBody> funcs, TreeSet<Location> 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)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user