fix: nasty issues with compilation

This commit is contained in:
TopchetoEU 2024-12-09 22:15:15 +02:00
parent ba7505e148
commit 4992d0211b
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
50 changed files with 487 additions and 116 deletions

235
src/compiler/main.ts Normal file
View File

@ -0,0 +1,235 @@
import { parse } from "acorn";
import {} from "acorn";
import { traverse } from "estraverse";
import { Declaration, Identifier, Node, VariableDeclaration } from "estree";
enum VariableType {
Var,
Let,
Const,
}
class Variable {
public constructor(
public readonly scope: Scope,
public readonly name: string,
public readonly type: VariableType,
public readonly readers = new Set<Node>(),
public readonly writers = new Set<Node>(),
) { }
public child(scope: Scope) {
return new Variable(scope, this.name, this.type, this.readers, this.writers);
}
public get writable() {
return this.type !== VariableType.Const;
}
}
class Scope {
private _locals = new Set<Variable>();
private _capturables = new Set<Variable>();
private _captures = new Set<Variable>();
private _localNames = new Map<string, Variable>();
private _captureNames = new Map<string, Variable>();
private _parentToChild = new Map<Variable, Variable>();
private _childToParent = new Map<Variable, Variable>();
public get locals() {
return Iterator.from(this._locals.values());
}
private _addCapture(v?: Variable) {
if (v != null && this._locals.delete(v)) {
this._capturables.add(v);
}
return v;
}
public capture(name: string) {
if (this._localNames.has(name)) return this._addCapture(this._localNames.get(name));
if (this._captureNames.has(name)) return this._addCapture(this._captureNames.get(name));
const parent = this.parent?.capture(name);
if (parent == null) return undefined;
const child = parent.child(this);
this._parentToChild.set(parent, child);
this._childToParent.set(child, parent);
this._captures.add(child);
this._captureNames.set(child.name, child);
}
public add(name: string, type: VariableType) {
let res = this.get(name, false);
if (res != null) return res;
res = new Variable(this, name, type);
this._locals.add(res);
this._localNames.set(name, res);
return res;
}
public get(name: string, capture = true): Variable | undefined {
if (this._localNames.has(name)) return this._localNames.get(name);
if (this._captureNames.has(name)) return this._captureNames.get(name);
if (capture) this.parent?.capture(name);
else return undefined;
}
public constructor(
public readonly major: boolean,
public readonly node: Node,
public readonly parent?: Scope,
) { }
}
class BiMap<A, B> implements Iterable<[A, B]> {
private _first = new Map<A, B>();
private _second = new Map<B, A>();
public get(val: A): B;
public get(val: B): A;
public get(val: any) {
if (this._first.has(val)) return this._first.get(val);
if (this._second.has(val)) return this._second.get(val);
if (this._same.has(val)) return val;
return undefined;
}
public set(a: A, b: B) {
this._first.set(a, b);
this._second.set(b, a);
return this;
}
public has(val: A | B) {
return this._first.has(val as any) || this._second.has(val as any);
}
public delete(val: A | B) {
if (this._first.has(val as any)) {
const second = this._first.get(val as any)!;
this._first.delete(val as any);
this._second.delete(second);
return true;
}
else if (this._second.has(val as any)) {
const first = this._second.get(val as any)!;
this._second.delete(val as any);
this._first.delete(first);
return true;
}
else return false;
}
public *[Symbol.iterator]() {
yield *this._first;
}
public *keys() {
yield *this._first.keys();
}
public *values() {
yield *this._second.keys();
}
public *entries() {
yield *this._first.entries();
}
}
class ResolutionContext {
public readonly variableRefs = new Map<Identifier, Scope>();
public readonly declarations = new BiMap<Variable, Declaration>();
public resolveVariables() {
for (const el of this.variableRefs) {
}
}
}
class NodeContext {
public node: Node = undefined!;
public path: Node[] = [];
public atPath(i: number) {
return this.path[this.path.length - 1 - i];
}
public scope: Scope = undefined!;
public declType?: VariableType;
}
interface Collector {
enter(ctx: NodeContext, root: ResolutionContext): void;
leave(ctx: NodeContext, root: ResolutionContext): void;
}
function collect(node: Node, root: ResolutionContext, ...collectors: Collector[]) {
const nodeCtx = new NodeContext();
const path: Node[] = [];
traverse(node, {
enter(node) {
nodeCtx.node = node;
nodeCtx.path.push(node);
for (let i = 0; i < collectors.length; i++) {
collectors[i].enter(nodeCtx, root);
}
},
leave(node) {
nodeCtx.node = node;
nodeCtx.path.pop();
for (let i = 0; i < collectors.length; i++) {
collectors[i].leave(nodeCtx, root);
}
},
});
}
function assertDefined(val: unknown): asserts val is {} {
if (val == null) throw new Error("Undefined or null expression");
}
const scopeCollector: Collector = {
enter(ctx, root) {
if (ctx.node.type === "BlockStatement") {
ctx.scope = new Scope(false, ctx.node, ctx.scope.parent);
}
else if (ctx.node.type === "VariableDeclaration") {
switch (ctx.node.kind) {
case "var": ctx.declType = VariableType.Var; break;
case "let": ctx.declType = VariableType.Let; break;
case "const": ctx.declType = VariableType.Const; break;
default: throw new Error(`Unknown variable type '${(ctx.node as any).kind}'`);
}
}
else if (ctx.node.type === "VariableDeclarator") {
ctx.scope.
}
else if (ctx.node.type === "ClassDeclaration") {
}
else if (ctx.node.type === "Identifier") {
}
},
leave(ctx, root) {
if (ctx.scope.node === ctx.node) {
assertDefined(ctx.scope.parent);
ctx.scope = ctx.scope.parent;
}
else if (ctx.node.type === "VariableDeclaration") {
ctx.declType = undefined;
}
},
};
const program = parse("const a = 10;", { ecmaVersion: "latest" }) as Node;
collect(program, domain, ...stage1);

View File

@ -1,7 +1,10 @@
package me.topchetoeu.jscript.compilation; package me.topchetoeu.jscript.compilation;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedList; import java.util.LinkedList;
import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.FunctionBody;
@ -17,18 +20,10 @@ import me.topchetoeu.jscript.compilation.scope.FunctionScope;
public final class CompileResult { public final class CompileResult {
public static final Key<Void> DEBUG_LOG = new Key<>(); public static final Key<Void> DEBUG_LOG = new Key<>();
public static final class ChildData {
public final int id;
public final CompileResult result;
public ChildData(int id, CompileResult result) {
this.result = result;
this.id = id;
}
}
public final List<Instruction> instructions; public final List<Instruction> instructions;
public final List<CompileResult> children; public final List<CompileResult> children;
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
public final FunctionMapBuilder map; public final FunctionMapBuilder map;
public final Environment env; public final Environment env;
public int length; public int length;
@ -69,9 +64,11 @@ public final class CompileResult {
setLocationAndDebug(instructions.size() - 1, loc, type); setLocationAndDebug(instructions.size() - 1, loc, type);
} }
public int addChild(CompileResult res) { public CompileResult addChild(FunctionNode node, CompileResult res) {
this.children.add(res); this.children.add(res);
return this.children.size() - 1; this.childrenMap.put(node, res);
this.childrenIndices.put(node, this.children.size() - 1);
return res;
} }
public Instruction[] instructions() { public Instruction[] instructions() {

View File

@ -19,6 +19,9 @@ public class CompoundNode extends Node {
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
for (var stm : statements) stm.resolve(target); for (var stm : statements) stm.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
for (var stm : statements) stm.compileFunctions(target);
}
public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) { public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) {
List<Node> statements = new ArrayList<Node>(); List<Node> statements = new ArrayList<Node>();

View File

@ -21,19 +21,22 @@ public abstract class FunctionNode extends Node {
public final String name(String fallback) { public final String name(String fallback) {
return this.name() != null ? this.name() : fallback; return this.name() != null ? this.name() : fallback;
} }
protected final int[] captures(CompileResult target) {
protected final int[] captures(int id, CompileResult target) { return target.childrenMap.get(this).scope.getCaptureIndices();
return target.children.get(id).scope.getCaptureIndices();
} }
protected final Environment rootEnv(Environment env) { protected final Environment rootEnv(Environment env) {
return env.get(JavaScript.COMPILE_ROOT); return env.get(JavaScript.COMPILE_ROOT);
} }
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { @Override public void resolve(CompileResult target) { }
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String selfName) {
var target = new CompileResult(env, scope, params.size()); var target = new CompileResult(env, scope, params.size());
var i = 0; var i = 0;
body.resolve(target);
for (var param : params) { for (var param : params) {
var index = scope.define(param.name); var index = scope.define(param.name);
@ -44,17 +47,17 @@ public abstract class FunctionNode extends Node {
// if (selfName != null && !scope.has(selfName, false)) { // if (selfName != null && !scope.has(selfName, false)) {
// var i = scope.defineSpecial(new Variable(selfName, true), end); // var i = scope.defineSpecial(new Variable(selfName, true), end);
// target.add(Instruction.loadCalled()); // t.add(Instruction.loadCalled());
// target.add(_i -> i.index().toInit()); // t.add(_i -> i.index().toInit());
// } // }
body.resolve(target); body.compileFunctions(target);
body.compile(target, lastReturn, BreakpointType.NONE); body.compile(target, lastReturn, BreakpointType.NONE);
return target; return target;
} }
public final CompileResult compileBody(CompileResult parent, String name, String selfName) { public final CompileResult compileBody(CompileResult parent, String selfName) {
return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName); return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, selfName);
} }
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);

View File

@ -16,10 +16,12 @@ public class FunctionStatementNode extends FunctionNode {
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
target.scope.define(new Variable(name, false)); target.scope.define(new Variable(name, false));
} }
@Override public void compileFunctions(CompileResult target) {
target.addChild(this, compileBody(target, name()));
}
@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)); target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc());;
target.add(VariableNode.toSet(target, end, this.name, false, true)); target.add(VariableNode.toSet(target, end, this.name, false, true));
if (pollute) target.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());
} }

View File

@ -12,9 +12,13 @@ public class FunctionValueNode extends FunctionNode {
@Override public String name() { return name; } @Override public String name() { return name; }
@Override public void compileFunctions(CompileResult target) {
target.addChild(this, compileBody(target, name()));
}
@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, name)); target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc()); if (!pollute) target.add(Instruction.discard());
} }
public FunctionValueNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) { public FunctionValueNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) {

View File

@ -32,7 +32,6 @@ import me.topchetoeu.jscript.compilation.values.ArrayNode;
import me.topchetoeu.jscript.compilation.values.GlobalThisNode; import me.topchetoeu.jscript.compilation.values.GlobalThisNode;
import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.RegexNode; import me.topchetoeu.jscript.compilation.values.RegexNode;
import me.topchetoeu.jscript.compilation.values.SuperNode;
import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.ThisNode;
import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.VariableNode;
import me.topchetoeu.jscript.compilation.values.constants.BoolNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode;
@ -114,7 +113,6 @@ public final class JavaScript {
if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n);
if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), 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("this")) return ParseRes.res(new ThisNode(loc), n);
if (id.result.equals("super")) return ParseRes.res(new SuperNode(loc), n);
if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(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("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
@ -260,7 +258,7 @@ public final class JavaScript {
env.add(COMPILE_ROOT, env); env.add(COMPILE_ROOT, env);
var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null); var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null);
var res = func.compileBody(env, new FunctionScope(true), true, null, null); var res = func.compileBody(env, new FunctionScope(true), true, null);
return res; return res;
} }

View File

@ -17,6 +17,8 @@ public abstract class Node {
compile(target, pollute, BreakpointType.NONE); compile(target, pollute, BreakpointType.NONE);
} }
public abstract void compileFunctions(CompileResult target);
public Location loc() { return loc; } public Location loc() { return loc; }
public void setLoc(Location loc) { this.loc = loc; } public void setLoc(Location loc) { this.loc = loc; }

View File

@ -23,6 +23,11 @@ public class VariableDeclareNode extends Node {
target.scope.define(entry.var.name); target.scope.define(entry.var.name);
} }
} }
@Override public void compileFunctions(CompileResult target) {
for (var pair : values) {
if (pair.value != null) pair.value.compileFunctions(target);
}
}
@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) {

View File

@ -14,7 +14,10 @@ import me.topchetoeu.jscript.compilation.Node;
public class BreakNode extends Node { public class BreakNode extends Node {
public final String label; public final String label;
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) {
if (!LabelContext.getBreak(target.env).jump(target)) { if (!LabelContext.getBreak(target.env).jump(target)) {
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
else throw new SyntaxException(loc(), "Illegal break statement"); else throw new SyntaxException(loc(), "Illegal break statement");

View File

@ -14,6 +14,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class ContinueNode extends Node { public class ContinueNode extends Node {
public final String label; public final String label;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (!LabelContext.getCont(target.env).jump(target)) { if (!LabelContext.getCont(target.env).jump(target)) {
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));

View File

@ -10,6 +10,9 @@ import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
public class DebugNode extends Node { public class DebugNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.debug()); target.add(Instruction.debug());
if (pollute) target.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());

View File

@ -16,6 +16,10 @@ public class DeleteNode extends Node {
public final Node key; public final Node key;
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
key.compileFunctions(target);
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
value.compile(target, true); value.compile(target, true);
key.compile(target, true); key.compile(target, true);

View File

@ -16,6 +16,10 @@ public class DoWhileNode extends Node {
public final Node condition, body; public final Node condition, body;
public final String label; public final String label;
@Override public void compileFunctions(CompileResult target) {
condition.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
body.resolve(target); body.resolve(target);
} }

View File

@ -25,6 +25,10 @@ public class ForInNode extends Node {
binding.resolve(target); binding.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
object.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
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));

View File

@ -23,29 +23,33 @@ public class ForNode extends Node {
declaration.resolve(target); declaration.resolve(target);
body.resolve(target); body.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
declaration.compileFunctions(target);
assignment.compileFunctions(target);
condition.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
var subtarget = target.subtarget(); declaration.compile(target, false, BreakpointType.STEP_OVER);
declaration.compile(subtarget, false, BreakpointType.STEP_OVER); int start = target.size();
CompoundNode.compileMultiEntry(condition, target, true, BreakpointType.STEP_OVER);
int start = subtarget.size(); int mid = target.temp();
CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER);
int mid = subtarget.temp();
var end = new DeferredIntSupplier(); var end = new DeferredIntSupplier();
LabelContext.pushLoop(subtarget.env, loc(), label, end, start); LabelContext.pushLoop(target.env, loc(), label, end, start);
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); CompoundNode.compileMultiEntry(assignment, target, false, BreakpointType.STEP_OVER);
int endI = subtarget.size(); int endI = target.size();
end.set(endI); end.set(endI);
LabelContext.popLoop(subtarget.env, label); LabelContext.popLoop(target.env, label);
subtarget.add(Instruction.jmp(start - endI)); target.add(Instruction.jmp(start - endI));
subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); target.set(mid, Instruction.jmpIfNot(endI - mid + 1));
if (pollute) subtarget.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());
} }
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

@ -20,7 +20,11 @@ public class IfNode extends Node {
body.resolve(target); body.resolve(target);
if (elseBody != null) elseBody.resolve(target); if (elseBody != null) elseBody.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
condition.compileFunctions(target);
body.compileFunctions(target);
if (elseBody != null) body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) { @Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) {
condition.compile(target, true, breakpoint); condition.compile(target, true, breakpoint);

View File

@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class ReturnNode extends Node { public class ReturnNode extends Node {
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
if (value != null) value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (value == null) target.add(Instruction.pushUndefined()); if (value == null) target.add(Instruction.pushUndefined());
else value.compile(target, true); else value.compile(target, true);

View File

@ -36,7 +36,15 @@ public class SwitchNode extends Node {
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
for (var stm : body) stm.resolve(target); for (var stm : body) stm.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
for (var _case : cases) {
_case.value.compileFunctions(target);
}
for (var stm : body) {
stm.compileFunctions(target);
}
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
var caseToStatement = new HashMap<Integer, Integer>(); var caseToStatement = new HashMap<Integer, Integer>();
var statementToIndex = new HashMap<Integer, Integer>(); var statementToIndex = new HashMap<Integer, Integer>();

View File

@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class ThrowNode extends Node { public class ThrowNode extends Node {
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
value.compile(target, true); value.compile(target, true);
target.add(Instruction.throwInstr()).setLocation(loc()); target.add(Instruction.throwInstr()).setLocation(loc());

View File

@ -25,7 +25,11 @@ public class TryNode extends Node {
if (catchBody != null) catchBody.resolve(target); if (catchBody != null) catchBody.resolve(target);
if (finallyBody != null) finallyBody.resolve(target); if (finallyBody != null) finallyBody.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
tryBody.compileFunctions(target);
if (catchBody != null) catchBody.compileFunctions(target);
if (finallyBody != null) finallyBody.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) { @Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
int replace = target.temp(); int replace = target.temp();
var endSuppl = new DeferredIntSupplier(); var endSuppl = new DeferredIntSupplier();

View File

@ -20,6 +20,10 @@ public class WhileNode extends Node {
@Override public void resolve(CompileResult target) { @Override public void resolve(CompileResult target) {
body.resolve(target); body.resolve(target);
} }
@Override public void compileFunctions(CompileResult target) {
condition.compileFunctions(target);
body.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
int start = target.size(); int start = target.size();
condition.compile(target, true); condition.compile(target, true);

View File

@ -17,6 +17,11 @@ public class FieldMemberNode implements Member {
@Override public Location loc() { return loc; } @Override public Location loc() { return loc; }
@Override public void compileFunctions(CompileResult target) {
key.compileFunctions(target);
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.dup()); if (pollute) target.add(Instruction.dup());
key.compile(target, true); key.compile(target, true);

View File

@ -6,5 +6,6 @@ import me.topchetoeu.jscript.compilation.CompileResult;
public interface Member { public interface Member {
Location loc(); Location loc();
void compileFunctions(CompileResult target);
void compile(CompileResult target, boolean pollute); void compile(CompileResult target, boolean pollute);
} }

View File

@ -32,12 +32,18 @@ public final class PropertyMemberNode extends FunctionNode implements Member {
public boolean isGetter() { return argument == null; } public boolean isGetter() { return argument == null; }
public boolean isSetter() { return argument != null; } public boolean isSetter() { return argument != null; }
@Override public void compileFunctions(CompileResult target) {
key.compileFunctions(target);
target.addChild(this, compileBody(target, null));
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (pollute) target.add(Instruction.dup()); if (pollute) target.add(Instruction.dup());
key.compile(target, true); key.compile(target, true);
var id = target.addChild(compileBody(target, name, null)); target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
target.add(Instruction.loadFunc(id, name(), captures(id, target))); target.add(VariableNode.toSet(target, end, name(name), false, true));
target.add(Instruction.defProp(isSetter()));
} }

View File

@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class ArgumentsNode extends Node { public class ArgumentsNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadArgs()); if (pollute) target.add(Instruction.loadArgs());
} }

View File

@ -15,6 +15,10 @@ import me.topchetoeu.jscript.compilation.Node;
public class ArrayNode extends Node { public class ArrayNode extends Node {
public final Node[] statements; public final Node[] statements;
@Override public void compileFunctions(CompileResult target) {
for (var stm : statements) stm.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadArr(statements.length)); target.add(Instruction.loadArr(statements.length));

View File

@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class GlobalThisNode extends Node { public class GlobalThisNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadGlob()); if (pollute) target.add(Instruction.loadGlob());
} }

View File

@ -20,6 +20,10 @@ import me.topchetoeu.jscript.compilation.values.constants.StringNode;
public class ObjectNode extends Node { public class ObjectNode extends Node {
public final List<Member> members; public final List<Member> members;
@Override public void compileFunctions(CompileResult target) {
for (var member : members) member.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadObj()); target.add(Instruction.loadObj());
for (var el : members) el.compile(target, true); for (var el : members) el.compile(target, true);

View File

@ -11,12 +11,14 @@ import me.topchetoeu.jscript.compilation.Node;
public class RegexNode extends Node { public class RegexNode extends Node {
public final String pattern, flags; public final String pattern, flags;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
target.add(Instruction.loadRegex(pattern, flags)); target.add(Instruction.loadRegex(pattern, flags));
if (!pollute) target.add(Instruction.discard()); if (!pollute) target.add(Instruction.discard());
} }
public static ParseRes<RegexNode> parse(Source src, int i) { public static ParseRes<RegexNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);

View File

@ -1,17 +0,0 @@
package me.topchetoeu.jscript.compilation.values;
import me.topchetoeu.jscript.common.SyntaxException;
import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Node;
public class SuperNode extends Node {
@Override public void compile(CompileResult target, boolean pollute) {
throw new SyntaxException(loc(), "Unexpected 'super' reference here");
}
public SuperNode(Location loc) {
super(loc);
}
}

View File

@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class ThisNode extends Node { public class ThisNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.loadThis()); if (pollute) target.add(Instruction.loadThis());
} }

View File

@ -13,6 +13,9 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget;
public class VariableNode extends Node implements ChangeTarget { public class VariableNode extends Node implements ChangeTarget {
public final String name; public final String name;
@Override public void compileFunctions(CompileResult target) {
}
public String assignName() { return name; } public String assignName() { return name; }
@Override public void beforeChange(CompileResult target) { @Override public void beforeChange(CompileResult target) {

View File

@ -8,6 +8,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class BoolNode extends Node { public class BoolNode extends Node {
public final boolean value; public final boolean value;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value)); if (pollute) target.add(Instruction.pushValue(value));
} }

View File

@ -6,6 +6,9 @@ import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
public class NullNode extends Node { public class NullNode extends Node {
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushNull()); if (pollute) target.add(Instruction.pushNull());
} }

View File

@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class NumberNode extends Node { public class NumberNode extends Node {
public final double value; public final double value;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value)); if (pollute) target.add(Instruction.pushValue(value));
} }

View File

@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node;
public class StringNode extends Node { public class StringNode extends Node {
public final String value; public final String value;
@Override public void compileFunctions(CompileResult target) {
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (pollute) target.add(Instruction.pushValue(value)); if (pollute) target.add(Instruction.pushValue(value));
} }

View File

@ -12,6 +12,11 @@ public class AssignNode extends Node implements AssignTarget {
public final AssignTarget assignable; public final AssignTarget assignable;
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
((Node)assignable).compileFunctions(target);
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (assignable instanceof AssignNode other) throw new SyntaxException(other.loc(), "Assign deconstructor not allowed here"); if (assignable instanceof AssignNode other) throw new SyntaxException(other.loc(), "Assign deconstructor not allowed here");

View File

@ -17,6 +17,11 @@ public class CallNode extends Node {
public final Node[] args; public final Node[] args;
public final boolean isNew; public final boolean isNew;
@Override public void compileFunctions(CompileResult target) {
func.compileFunctions(target);
for (var arg : args) arg.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) {
if (!isNew && func instanceof IndexNode indexNode) { if (!isNew && func instanceof IndexNode indexNode) {
var obj = indexNode.object; var obj = indexNode.object;

View File

@ -17,6 +17,11 @@ public class ChangeNode extends Node {
public final Node value; public final Node value;
public final Operation op; public final Operation op;
@Override public void compileFunctions(CompileResult target) {
((Node)changable).compileFunctions(target);
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
changable.beforeChange(target); changable.beforeChange(target);
value.compile(target, true); value.compile(target, true);

View File

@ -13,6 +13,10 @@ import me.topchetoeu.jscript.compilation.Node;
public class DiscardNode extends Node { public class DiscardNode extends Node {
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (value != null) value.compile(target, false); if (value != null) value.compile(target, false);
if (pollute) target.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());

View File

@ -17,6 +17,11 @@ public class IndexNode extends Node implements ChangeTarget {
public final Node object; public final Node object;
public final Node index; public final Node index;
@Override public void compileFunctions(CompileResult target) {
object.compileFunctions(target);
index.compileFunctions(target);
}
@Override public void beforeAssign(CompileResult target) { @Override public void beforeAssign(CompileResult target) {
object.compile(target, true); object.compile(target, true);
indexStorePushKey(target, index); indexStorePushKey(target, index);

View File

@ -12,6 +12,11 @@ import me.topchetoeu.jscript.compilation.Node;
public class LazyAndNode extends Node { public class LazyAndNode extends Node {
public final Node first, second; public final Node first, second;
@Override public void compileFunctions(CompileResult target) {
first.compileFunctions(target);
second.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
first.compile(target, true); first.compile(target, true);
if (pollute) target.add(Instruction.dup()); if (pollute) target.add(Instruction.dup());

View File

@ -13,6 +13,11 @@ import me.topchetoeu.jscript.compilation.Node;
public class LazyOrNode extends Node { public class LazyOrNode extends Node {
public final Node first, second; public final Node first, second;
@Override public void compileFunctions(CompileResult target) {
first.compileFunctions(target);
second.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
first.compile(target, true); first.compile(target, true);
if (pollute) target.add(Instruction.dup()); if (pollute) target.add(Instruction.dup());

View File

@ -105,6 +105,10 @@ public class OperationNode extends Node {
public final Node[] args; public final Node[] args;
public final Operation operation; public final Operation operation;
@Override public void compileFunctions(CompileResult target) {
for (var arg : args) arg.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
for (var arg : args) { for (var arg : args) {
arg.compile(target, true); arg.compile(target, true);

View File

@ -12,6 +12,10 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget;
import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
public class PostfixNode extends ChangeNode { public class PostfixNode extends ChangeNode {
@Override public void compileFunctions(CompileResult target) {
((Node)changable).compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
super.compile(target, pollute); super.compile(target, pollute);

View File

@ -14,6 +14,10 @@ import me.topchetoeu.jscript.compilation.values.VariableNode;
public class TypeofNode extends Node { public class TypeofNode extends Node {
public final Node value; public final Node value;
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
}
@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, true)); target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true));

View File

@ -13,6 +13,10 @@ public class VariableAssignNode extends Node {
public final Node value; public final Node value;
public final Operation operation; public final Operation operation;
@Override public void compileFunctions(CompileResult target) {
value.compileFunctions(target);
}
@Override public void compile(CompileResult target, boolean pollute) { @Override public void compile(CompileResult target, boolean pollute) {
if (operation != null) { if (operation != null) {
target.add(VariableNode.toGet(target, loc(), name)); target.add(VariableNode.toGet(target, loc(), name));

View File

@ -13,6 +13,7 @@ import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.json.JSON;
import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.Filename;
import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.debug.DebugContext;
import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.exceptions.EngineException;
import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
@ -278,7 +279,7 @@ public class SimpleRepl {
int[] i = new int[1]; int[] i = new int[1];
res.defineOwnMember(env, "setGlobalPrototypes", new NativeFunction(args -> { res.defineOwnMember(env, "setGlobalPrototypes", new NativeFunction(args -> {
var obj = (ObjectValue)args.get(1); var obj = (ObjectValue)args.get(0);
setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); setProto(args.env, env, Value.OBJECT_PROTO, obj, "object");
setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function"); setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function");
@ -313,6 +314,7 @@ public class SimpleRepl {
environment.add(EventLoop.KEY, engine); environment.add(EventLoop.KEY, engine);
environment.add(DebugContext.KEY, new DebugContext()); environment.add(DebugContext.KEY, new DebugContext());
environment.add(Compiler.KEY, Compiler.DEFAULT); environment.add(Compiler.KEY, Compiler.DEFAULT);
// environment.add(CompileResult.DEBUG_LOG);
var glob = Value.global(environment); var glob = Value.global(environment);

View File

@ -1,20 +1,13 @@
return;
(function main() { (function main() {
var __extends = (this && this.__extends) || (function () { function extend(derived, base) {
var extendStatics = function (d, b) { if (base == null) {
extendStatics = Object.setPrototypeOf || object.setPrototype(derived.prototype, null);
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || }
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; else {
return extendStatics(d, b); object.setPrototype(derived, base);
}; object.setPrototype(derived.prototype, base.prototype);
return function (d, b) { }
if (typeof b !== "function" && b !== null) }
throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
extendStatics(d, b);
function __() { this.constructor = d; }
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
};
})();
var target = arguments[0]; var target = arguments[0];
var primordials = arguments[1]; var primordials = arguments[1];
@ -68,9 +61,8 @@ return;
parse: function (val) { throw new Error("JSON parse not polyfillable"); }, parse: function (val) { throw new Error("JSON parse not polyfillable"); },
} }
var setGlobalPrototypes = primordials.setGlobalPrototype; var setGlobalPrototypes = primordials.setGlobalPrototypes;
var compile = primordials.compile; var compile = primordials.compile;
var setIntrinsic = primordials.setIntrinsic;
var valueKey = symbol.makeSymbol("Primitive.value"); var valueKey = symbol.makeSymbol("Primitive.value");
var undefined = void 0; var undefined = void 0;
target.undefined = undefined; target.undefined = undefined;
@ -88,26 +80,22 @@ return;
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
} }
function wrapIndex(i, len) { } function wrapIndex(i, len) { }
var Symbol = /** @class */ (function () { function Symbol(name) {
function Symbol(name) { if (name === undefined) name = "";
if (name === undefined) name = ""; return symbol.makeSymbol(name);
return symbol.makeSymbol(name); }
} Symbol.prototype.toString = function () {
Symbol.prototype.toString = function () { return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")";
return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; };
}; Symbol.prototype.valueOf = function () {
Symbol.prototype.valueOf = function () { return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf");
return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); };
}; Symbol.for = function (name) {
Symbol.for = function (name) { return symbol.getSymbol(name + "");
return symbol.getSymbol(name + ""); };
}; Symbol.keyFor = function (value) {
Symbol.keyFor = function (value) { return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor"));
return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor")); };
};
return Symbol;
}());
;
object.defineProperty(Symbol.prototype, "desc", false, true, function () { object.defineProperty(Symbol.prototype, "desc", false, true, function () {
return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
}); });
@ -120,7 +108,8 @@ return;
object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split"));
object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag"));
func.setConstructable(Symbol, false); func.setConstructable(Symbol, false);
target.Symbol = Symbol;
function Number(value) { function Number(value) {
if (func.invokeType(arguments, this) === "call") { if (func.invokeType(arguments, this) === "call") {
if (arguments.length === 0) return 0; if (arguments.length === 0) return 0;
@ -290,6 +279,7 @@ return;
return obj; return obj;
}; };
func.setCallable(Object, true); func.setCallable(Object, true);
extend(Object, null);
object.setPrototype(Object.prototype, null); object.setPrototype(Object.prototype, null);
target.Object = Object; target.Object = Object;
@ -351,7 +341,7 @@ return;
} }
func.setCallable(Array, true); func.setCallable(Array, true);
target.Array = Array; target.Array = Array;
function Error(msg) { function Error(msg) {
if (msg === void 0) { msg = ""; } if (msg === void 0) { msg = ""; }
if (func.invokeType(arguments, this) === "call") if (func.invokeType(arguments, this) === "call")
@ -369,8 +359,8 @@ return;
object.defineField(Error.prototype, "message", true, false, true, ""); object.defineField(Error.prototype, "message", true, false, true, "");
func.setCallable(Error, true); func.setCallable(Error, true);
target.Error = Error; target.Error = Error;
__extends(SyntaxError, Error); extend(SyntaxError, Error);
function SyntaxError(msg) { function SyntaxError(msg) {
if (func.invokeType(arguments, this) === "call") if (func.invokeType(arguments, this) === "call")
return new SyntaxError(msg); return new SyntaxError(msg);
@ -379,8 +369,8 @@ return;
object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError");
func.setCallable(SyntaxError, true); func.setCallable(SyntaxError, true);
target.SyntaxError = SyntaxError; target.SyntaxError = SyntaxError;
__extends(TypeError, Error); extend(TypeError, Error);
function TypeError(msg) { function TypeError(msg) {
if (func.invokeType(arguments, this) === "call") if (func.invokeType(arguments, this) === "call")
return new TypeError(msg); return new TypeError(msg);
@ -389,8 +379,8 @@ return;
object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); object.defineField(TypeError.prototype, "name", true, false, true, "TypeError");
func.setCallable(TypeError, true); func.setCallable(TypeError, true);
target.TypeError = TypeError; target.TypeError = TypeError;
__extends(RangeError, Error); extend(RangeError, Error);
function RangeError(msg) { function RangeError(msg) {
if (func.invokeType(arguments, this) === "call") if (func.invokeType(arguments, this) === "call")
return new RangeError(msg); return new RangeError(msg);
@ -399,9 +389,9 @@ return;
object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); object.defineField(RangeError.prototype, "name", true, false, true, "RangeError");
func.setCallable(RangeError, true); func.setCallable(RangeError, true);
target.RangeError = RangeError; target.RangeError = RangeError;
target.uint8 = primordials.uint8; target.uint8 = primordials.uint8;
setGlobalPrototypes({ setGlobalPrototypes({
string: String.prototype, string: String.prototype,
number: Number.prototype, number: Number.prototype,