Add support for source mappings #10

Merged
TopchetoEU merged 22 commits from TopchetoEU/mapping into master 2023-12-18 20:42:38 +00:00
5 changed files with 83 additions and 66 deletions
Showing only changes of commit fe86123f0f - Show all commits

View File

@ -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];

View File

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

View File

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

View File

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

View File

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