fix: nasty issues with compilation
This commit is contained in:
parent
ba7505e148
commit
4992d0211b
235
src/compiler/main.ts
Normal file
235
src/compiler/main.ts
Normal 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);
|
@ -1,7 +1,10 @@
|
||||
package me.topchetoeu.jscript.compilation;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
|
||||
import me.topchetoeu.jscript.common.FunctionBody;
|
||||
@ -17,18 +20,10 @@ import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||
public final class CompileResult {
|
||||
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<CompileResult> children;
|
||||
public final Map<FunctionNode, CompileResult> childrenMap = new HashMap<>();
|
||||
public final Map<FunctionNode, Integer> childrenIndices = new HashMap<>();
|
||||
public final FunctionMapBuilder map;
|
||||
public final Environment env;
|
||||
public int length;
|
||||
@ -69,9 +64,11 @@ public final class CompileResult {
|
||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||
}
|
||||
|
||||
public int addChild(CompileResult res) {
|
||||
public CompileResult addChild(FunctionNode node, CompileResult 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() {
|
||||
|
@ -19,6 +19,9 @@ public class CompoundNode extends Node {
|
||||
@Override public void resolve(CompileResult 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) {
|
||||
List<Node> statements = new ArrayList<Node>();
|
||||
|
@ -21,19 +21,22 @@ public abstract class FunctionNode extends Node {
|
||||
public final String name(String fallback) {
|
||||
return this.name() != null ? this.name() : fallback;
|
||||
}
|
||||
|
||||
protected final int[] captures(int id, CompileResult target) {
|
||||
return target.children.get(id).scope.getCaptureIndices();
|
||||
protected final int[] captures(CompileResult target) {
|
||||
return target.childrenMap.get(this).scope.getCaptureIndices();
|
||||
}
|
||||
|
||||
protected final Environment rootEnv(Environment env) {
|
||||
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 i = 0;
|
||||
|
||||
body.resolve(target);
|
||||
|
||||
for (var param : params) {
|
||||
var index = scope.define(param.name);
|
||||
|
||||
@ -44,17 +47,17 @@ public abstract class FunctionNode extends Node {
|
||||
// if (selfName != null && !scope.has(selfName, false)) {
|
||||
// var i = scope.defineSpecial(new Variable(selfName, true), end);
|
||||
|
||||
// target.add(Instruction.loadCalled());
|
||||
// target.add(_i -> i.index().toInit());
|
||||
// t.add(Instruction.loadCalled());
|
||||
// t.add(_i -> i.index().toInit());
|
||||
// }
|
||||
|
||||
body.resolve(target);
|
||||
body.compileFunctions(target);
|
||||
body.compile(target, lastReturn, BreakpointType.NONE);
|
||||
|
||||
return target;
|
||||
}
|
||||
public final CompileResult compileBody(CompileResult parent, String name, String selfName) {
|
||||
return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName);
|
||||
public final CompileResult compileBody(CompileResult parent, String 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);
|
||||
|
@ -16,10 +16,12 @@ public class FunctionStatementNode extends FunctionNode {
|
||||
@Override public void resolve(CompileResult target) {
|
||||
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) {
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc());;
|
||||
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||
target.add(VariableNode.toSet(target, end, this.name, false, true));
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
}
|
||||
|
@ -12,9 +12,13 @@ public class FunctionValueNode extends FunctionNode {
|
||||
|
||||
@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) {
|
||||
var id = target.addChild(compileBody(target, name, name));
|
||||
target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc());
|
||||
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
public FunctionValueNode(Location loc, Location end, List<VariableNode> params, CompoundNode body, String name) {
|
||||
|
@ -32,7 +32,6 @@ 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.SuperNode;
|
||||
import me.topchetoeu.jscript.compilation.values.ThisNode;
|
||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
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("null")) return ParseRes.res(new NullNode(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("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n);
|
||||
|
||||
@ -260,7 +258,7 @@ public final class JavaScript {
|
||||
env.add(COMPILE_ROOT, env);
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
|
@ -17,6 +17,8 @@ public abstract class Node {
|
||||
compile(target, pollute, BreakpointType.NONE);
|
||||
}
|
||||
|
||||
public abstract void compileFunctions(CompileResult target);
|
||||
|
||||
public Location loc() { return loc; }
|
||||
public void setLoc(Location loc) { this.loc = loc; }
|
||||
|
||||
|
@ -23,6 +23,11 @@ public class VariableDeclareNode extends Node {
|
||||
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) {
|
||||
for (var entry : values) {
|
||||
if (entry.value != null) {
|
||||
|
@ -14,6 +14,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class BreakNode extends Node {
|
||||
public final String label;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (!LabelContext.getBreak(target.env).jump(target)) {
|
||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||
|
@ -14,6 +14,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class ContinueNode extends Node {
|
||||
public final String label;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (!LabelContext.getCont(target.env).jump(target)) {
|
||||
if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label));
|
||||
|
@ -10,6 +10,9 @@ import me.topchetoeu.jscript.compilation.JavaScript;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
|
||||
public class DebugNode extends Node {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.debug());
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
|
@ -16,6 +16,10 @@ public class DeleteNode extends Node {
|
||||
public final Node key;
|
||||
public final Node value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
key.compileFunctions(target);
|
||||
value.compileFunctions(target);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
value.compile(target, true);
|
||||
key.compile(target, true);
|
||||
|
@ -16,6 +16,10 @@ public class DoWhileNode extends Node {
|
||||
public final Node condition, body;
|
||||
public final String label;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
condition.compileFunctions(target);
|
||||
body.compileFunctions(target);
|
||||
}
|
||||
@Override public void resolve(CompileResult target) {
|
||||
body.resolve(target);
|
||||
}
|
||||
|
@ -25,6 +25,10 @@ public class ForInNode extends Node {
|
||||
binding.resolve(target);
|
||||
}
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
object.compileFunctions(target);
|
||||
body.compileFunctions(target);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||
target.add(Instruction.keys(false, true));
|
||||
|
@ -23,29 +23,33 @@ public class ForNode extends Node {
|
||||
declaration.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) {
|
||||
var subtarget = target.subtarget();
|
||||
declaration.compile(target, false, BreakpointType.STEP_OVER);
|
||||
|
||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||
|
||||
int start = subtarget.size();
|
||||
CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER);
|
||||
int mid = subtarget.temp();
|
||||
int start = target.size();
|
||||
CompoundNode.compileMultiEntry(condition, target, true, BreakpointType.STEP_OVER);
|
||||
int mid = target.temp();
|
||||
|
||||
var end = new DeferredIntSupplier();
|
||||
|
||||
LabelContext.pushLoop(subtarget.env, loc(), label, end, start);
|
||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||
LabelContext.pushLoop(target.env, loc(), label, end, start);
|
||||
CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER);
|
||||
|
||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
||||
int endI = subtarget.size();
|
||||
CompoundNode.compileMultiEntry(assignment, target, false, BreakpointType.STEP_OVER);
|
||||
int endI = target.size();
|
||||
|
||||
end.set(endI);
|
||||
LabelContext.popLoop(subtarget.env, label);
|
||||
LabelContext.popLoop(target.env, label);
|
||||
|
||||
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());
|
||||
}
|
||||
|
||||
public ForNode(Location loc, String label, Node declaration, Node condition, Node assignment, Node body) {
|
||||
|
@ -20,7 +20,11 @@ public class IfNode extends Node {
|
||||
body.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) {
|
||||
condition.compile(target, true, breakpoint);
|
||||
|
||||
|
@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class ReturnNode extends Node {
|
||||
public final Node value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
if (value != null) value.compileFunctions(target);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (value == null) target.add(Instruction.pushUndefined());
|
||||
else value.compile(target, true);
|
||||
|
@ -36,7 +36,15 @@ public class SwitchNode extends Node {
|
||||
@Override public void resolve(CompileResult 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) {
|
||||
var caseToStatement = new HashMap<Integer, Integer>();
|
||||
var statementToIndex = new HashMap<Integer, Integer>();
|
||||
|
@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class ThrowNode extends Node {
|
||||
public final Node value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
value.compileFunctions(target);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
value.compile(target, true);
|
||||
target.add(Instruction.throwInstr()).setLocation(loc());
|
||||
|
@ -25,7 +25,11 @@ public class TryNode extends Node {
|
||||
if (catchBody != null) catchBody.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) {
|
||||
int replace = target.temp();
|
||||
var endSuppl = new DeferredIntSupplier();
|
||||
|
@ -20,6 +20,10 @@ public class WhileNode extends Node {
|
||||
@Override public void resolve(CompileResult target) {
|
||||
body.resolve(target);
|
||||
}
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
condition.compileFunctions(target);
|
||||
body.compileFunctions(target);
|
||||
}
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
int start = target.size();
|
||||
condition.compile(target, true);
|
||||
|
@ -17,6 +17,11 @@ public class FieldMemberNode implements Member {
|
||||
|
||||
@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) {
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
key.compile(target, true);
|
||||
|
@ -6,5 +6,6 @@ import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
public interface Member {
|
||||
Location loc();
|
||||
|
||||
void compileFunctions(CompileResult target);
|
||||
void compile(CompileResult target, boolean pollute);
|
||||
}
|
||||
|
@ -32,12 +32,18 @@ public final class PropertyMemberNode extends FunctionNode implements Member {
|
||||
public boolean isGetter() { 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) {
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
key.compile(target, true);
|
||||
|
||||
var id = target.addChild(compileBody(target, name, null));
|
||||
target.add(Instruction.loadFunc(id, name(), captures(id, target)));
|
||||
target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc());
|
||||
target.add(VariableNode.toSet(target, end, name(name), false, true));
|
||||
target.add(Instruction.defProp(isSetter()));
|
||||
}
|
||||
|
||||
|
||||
|
@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
|
||||
|
||||
public class ArgumentsNode extends Node {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.loadArgs());
|
||||
}
|
||||
|
@ -15,6 +15,10 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class ArrayNode extends Node {
|
||||
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) {
|
||||
target.add(Instruction.loadArr(statements.length));
|
||||
|
||||
|
@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
|
||||
|
||||
public class GlobalThisNode extends Node {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.loadGlob());
|
||||
}
|
||||
|
@ -20,6 +20,10 @@ import me.topchetoeu.jscript.compilation.values.constants.StringNode;
|
||||
public class ObjectNode extends Node {
|
||||
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) {
|
||||
target.add(Instruction.loadObj());
|
||||
for (var el : members) el.compile(target, true);
|
||||
|
@ -11,12 +11,14 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class RegexNode extends Node {
|
||||
public final String pattern, flags;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
target.add(Instruction.loadRegex(pattern, flags));
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
|
||||
|
||||
public static ParseRes<RegexNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
|
||||
|
||||
public class ThisNode extends Node {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.loadThis());
|
||||
}
|
||||
|
@ -13,6 +13,9 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget;
|
||||
public class VariableNode extends Node implements ChangeTarget {
|
||||
public final String name;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
public String assignName() { return name; }
|
||||
|
||||
@Override public void beforeChange(CompileResult target) {
|
||||
|
@ -8,6 +8,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class BoolNode extends Node {
|
||||
public final boolean value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.pushValue(value));
|
||||
}
|
||||
|
@ -6,6 +6,9 @@ import me.topchetoeu.jscript.compilation.CompileResult;
|
||||
import me.topchetoeu.jscript.compilation.Node;
|
||||
|
||||
public class NullNode extends Node {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.pushNull());
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class NumberNode extends Node {
|
||||
public final double value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.pushValue(value));
|
||||
}
|
||||
|
@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class StringNode extends Node {
|
||||
public final String value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (pollute) target.add(Instruction.pushValue(value));
|
||||
}
|
||||
|
@ -12,6 +12,11 @@ public class AssignNode extends Node implements AssignTarget {
|
||||
public final AssignTarget assignable;
|
||||
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) {
|
||||
if (assignable instanceof AssignNode other) throw new SyntaxException(other.loc(), "Assign deconstructor not allowed here");
|
||||
|
||||
|
@ -17,6 +17,11 @@ public class CallNode extends Node {
|
||||
public final Node[] args;
|
||||
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) {
|
||||
if (!isNew && func instanceof IndexNode indexNode) {
|
||||
var obj = indexNode.object;
|
||||
|
@ -17,6 +17,11 @@ public class ChangeNode extends Node {
|
||||
public final Node value;
|
||||
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) {
|
||||
changable.beforeChange(target);
|
||||
value.compile(target, true);
|
||||
|
@ -13,6 +13,10 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class DiscardNode extends Node {
|
||||
public final Node value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
value.compileFunctions(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (value != null) value.compile(target, false);
|
||||
if (pollute) target.add(Instruction.pushUndefined());
|
||||
|
@ -17,6 +17,11 @@ public class IndexNode extends Node implements ChangeTarget {
|
||||
public final Node object;
|
||||
public final Node index;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
object.compileFunctions(target);
|
||||
index.compileFunctions(target);
|
||||
}
|
||||
|
||||
@Override public void beforeAssign(CompileResult target) {
|
||||
object.compile(target, true);
|
||||
indexStorePushKey(target, index);
|
||||
|
@ -12,6 +12,11 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class LazyAndNode extends Node {
|
||||
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) {
|
||||
first.compile(target, true);
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
|
@ -13,6 +13,11 @@ import me.topchetoeu.jscript.compilation.Node;
|
||||
public class LazyOrNode extends Node {
|
||||
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) {
|
||||
first.compile(target, true);
|
||||
if (pollute) target.add(Instruction.dup());
|
||||
|
@ -105,6 +105,10 @@ public class OperationNode extends Node {
|
||||
public final Node[] args;
|
||||
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) {
|
||||
for (var arg : args) {
|
||||
arg.compile(target, true);
|
||||
|
@ -12,6 +12,10 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget;
|
||||
import me.topchetoeu.jscript.compilation.values.constants.NumberNode;
|
||||
|
||||
public class PostfixNode extends ChangeNode {
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
((Node)changable).compileFunctions(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
super.compile(target, pollute);
|
||||
|
||||
|
@ -14,6 +14,10 @@ import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||
public class TypeofNode extends Node {
|
||||
public final Node value;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
value.compileFunctions(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (value instanceof VariableNode varNode) {
|
||||
target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true));
|
||||
|
@ -13,6 +13,10 @@ public class VariableAssignNode extends Node {
|
||||
public final Node value;
|
||||
public final Operation operation;
|
||||
|
||||
@Override public void compileFunctions(CompileResult target) {
|
||||
value.compileFunctions(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
if (operation != null) {
|
||||
target.add(VariableNode.toGet(target, loc(), name));
|
||||
|
@ -13,6 +13,7 @@ import me.topchetoeu.jscript.common.environment.Environment;
|
||||
import me.topchetoeu.jscript.common.environment.Key;
|
||||
import me.topchetoeu.jscript.common.json.JSON;
|
||||
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.exceptions.EngineException;
|
||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||
@ -278,7 +279,7 @@ public class SimpleRepl {
|
||||
int[] i = new int[1];
|
||||
|
||||
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.FUNCTION_PROTO, obj, "function");
|
||||
@ -313,6 +314,7 @@ public class SimpleRepl {
|
||||
environment.add(EventLoop.KEY, engine);
|
||||
environment.add(DebugContext.KEY, new DebugContext());
|
||||
environment.add(Compiler.KEY, Compiler.DEFAULT);
|
||||
// environment.add(CompileResult.DEBUG_LOG);
|
||||
|
||||
var glob = Value.global(environment);
|
||||
|
||||
|
@ -1,20 +1,13 @@
|
||||
return;
|
||||
(function main() {
|
||||
var __extends = (this && this.__extends) || (function () {
|
||||
var extendStatics = function (d, b) {
|
||||
extendStatics = Object.setPrototypeOf ||
|
||||
({ __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]; };
|
||||
return extendStatics(d, b);
|
||||
};
|
||||
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 __());
|
||||
};
|
||||
})();
|
||||
function extend(derived, base) {
|
||||
if (base == null) {
|
||||
object.setPrototype(derived.prototype, null);
|
||||
}
|
||||
else {
|
||||
object.setPrototype(derived, base);
|
||||
object.setPrototype(derived.prototype, base.prototype);
|
||||
}
|
||||
}
|
||||
|
||||
var target = arguments[0];
|
||||
var primordials = arguments[1];
|
||||
@ -68,9 +61,8 @@ return;
|
||||
parse: function (val) { throw new Error("JSON parse not polyfillable"); },
|
||||
}
|
||||
|
||||
var setGlobalPrototypes = primordials.setGlobalPrototype;
|
||||
var setGlobalPrototypes = primordials.setGlobalPrototypes;
|
||||
var compile = primordials.compile;
|
||||
var setIntrinsic = primordials.setIntrinsic;
|
||||
var valueKey = symbol.makeSymbol("Primitive.value");
|
||||
var undefined = void 0;
|
||||
target.undefined = undefined;
|
||||
@ -88,7 +80,6 @@ return;
|
||||
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
|
||||
}
|
||||
function wrapIndex(i, len) { }
|
||||
var Symbol = /** @class */ (function () {
|
||||
function Symbol(name) {
|
||||
if (name === undefined) name = "";
|
||||
return symbol.makeSymbol(name);
|
||||
@ -105,9 +96,6 @@ return;
|
||||
Symbol.keyFor = function (value) {
|
||||
return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor"));
|
||||
};
|
||||
return Symbol;
|
||||
}());
|
||||
;
|
||||
object.defineProperty(Symbol.prototype, "desc", false, true, function () {
|
||||
return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description"));
|
||||
});
|
||||
@ -120,6 +108,7 @@ return;
|
||||
object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split"));
|
||||
object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag"));
|
||||
func.setConstructable(Symbol, false);
|
||||
target.Symbol = Symbol;
|
||||
|
||||
function Number(value) {
|
||||
if (func.invokeType(arguments, this) === "call") {
|
||||
@ -290,6 +279,7 @@ return;
|
||||
return obj;
|
||||
};
|
||||
func.setCallable(Object, true);
|
||||
extend(Object, null);
|
||||
object.setPrototype(Object.prototype, null);
|
||||
target.Object = Object;
|
||||
|
||||
@ -370,7 +360,7 @@ return;
|
||||
func.setCallable(Error, true);
|
||||
target.Error = Error;
|
||||
|
||||
__extends(SyntaxError, Error);
|
||||
extend(SyntaxError, Error);
|
||||
function SyntaxError(msg) {
|
||||
if (func.invokeType(arguments, this) === "call")
|
||||
return new SyntaxError(msg);
|
||||
@ -380,7 +370,7 @@ return;
|
||||
func.setCallable(SyntaxError, true);
|
||||
target.SyntaxError = SyntaxError;
|
||||
|
||||
__extends(TypeError, Error);
|
||||
extend(TypeError, Error);
|
||||
function TypeError(msg) {
|
||||
if (func.invokeType(arguments, this) === "call")
|
||||
return new TypeError(msg);
|
||||
@ -390,7 +380,7 @@ return;
|
||||
func.setCallable(TypeError, true);
|
||||
target.TypeError = TypeError;
|
||||
|
||||
__extends(RangeError, Error);
|
||||
extend(RangeError, Error);
|
||||
function RangeError(msg) {
|
||||
if (func.invokeType(arguments, this) === "call")
|
||||
return new RangeError(msg);
|
||||
|
Loading…
Reference in New Issue
Block a user