Optimize var flattening #27
@ -3,12 +3,13 @@ package me.topchetoeu.jscript.common;
|
|||||||
public class FunctionBody {
|
public class FunctionBody {
|
||||||
public final FunctionBody[] children;
|
public final FunctionBody[] children;
|
||||||
public final Instruction[] instructions;
|
public final Instruction[] instructions;
|
||||||
public final int localsN, capturesN, length;
|
public final int localsN, capturablesN, capturesN, length;
|
||||||
|
|
||||||
public FunctionBody(int localsN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
|
public FunctionBody(int localsN, int capturablesN, int capturesN, int length, Instruction[] instructions, FunctionBody[] children) {
|
||||||
this.children = children;
|
this.children = children;
|
||||||
this.length = length;
|
this.length = length;
|
||||||
this.localsN = localsN;
|
this.localsN = localsN;
|
||||||
|
this.capturablesN = capturablesN;
|
||||||
this.capturesN = capturesN;
|
this.capturesN = capturesN;
|
||||||
this.instructions = instructions;
|
this.instructions = instructions;
|
||||||
}
|
}
|
||||||
|
@ -35,20 +35,21 @@ public class Instruction {
|
|||||||
LOAD_FUNC(0x30),
|
LOAD_FUNC(0x30),
|
||||||
LOAD_ARR(0x31),
|
LOAD_ARR(0x31),
|
||||||
LOAD_OBJ(0x32),
|
LOAD_OBJ(0x32),
|
||||||
LOAD_GLOB(0x33),
|
LOAD_REGEX(0x33),
|
||||||
LOAD_INTRINSICS(0x34),
|
|
||||||
LOAD_REGEX(0x35),
|
LOAD_GLOB(0x38),
|
||||||
|
LOAD_INTRINSICS(0x39),
|
||||||
|
LOAD_ARGS(0x3A),
|
||||||
|
LOAD_REST_ARGS(0x3B),
|
||||||
|
LOAD_CALLEE(0x3C),
|
||||||
|
LOAD_THIS(0x3D),
|
||||||
|
LOAD_ERROR(0x3E),
|
||||||
|
|
||||||
LOAD_VAR(0x40),
|
LOAD_VAR(0x40),
|
||||||
LOAD_MEMBER(0x41),
|
LOAD_MEMBER(0x41),
|
||||||
LOAD_MEMBER_INT(0x42),
|
LOAD_MEMBER_INT(0x42),
|
||||||
LOAD_MEMBER_STR(0x43),
|
LOAD_MEMBER_STR(0x43),
|
||||||
|
|
||||||
LOAD_ARGS(0x44),
|
|
||||||
LOAD_REST_ARGS(0x45),
|
|
||||||
LOAD_CALLEE(0x46),
|
|
||||||
LOAD_THIS(0x47),
|
|
||||||
|
|
||||||
STORE_VAR(0x48),
|
STORE_VAR(0x48),
|
||||||
STORE_MEMBER(0x49),
|
STORE_MEMBER(0x49),
|
||||||
STORE_MEMBER_INT(0x4A),
|
STORE_MEMBER_INT(0x4A),
|
||||||
@ -368,6 +369,9 @@ public class Instruction {
|
|||||||
public static Instruction loadIntrinsics(String key) {
|
public static Instruction loadIntrinsics(String key) {
|
||||||
return new Instruction(Type.LOAD_INTRINSICS, key);
|
return new Instruction(Type.LOAD_INTRINSICS, key);
|
||||||
}
|
}
|
||||||
|
public static Instruction loadError() {
|
||||||
|
return new Instruction(Type.LOAD_ERROR);
|
||||||
|
}
|
||||||
public static Instruction loadMember() {
|
public static Instruction loadMember() {
|
||||||
return new Instruction(Type.LOAD_MEMBER);
|
return new Instruction(Type.LOAD_MEMBER);
|
||||||
}
|
}
|
||||||
@ -457,12 +461,15 @@ public class Instruction {
|
|||||||
return new Instruction(Type.OPERATION, op);
|
return new Instruction(Type.OPERATION, op);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Instruction stackAlloc(int n) {
|
public static Instruction stackAlloc(int start, int n) {
|
||||||
return new Instruction(Type.STACK_ALLOC, n);
|
return new Instruction(Type.STACK_ALLOC, start, start + n);
|
||||||
}
|
}
|
||||||
public static Instruction stackRealloc(int n) {
|
public static Instruction stackRealloc(int start, int n) {
|
||||||
return new Instruction(Type.STACK_REALLOC, n);
|
return new Instruction(Type.STACK_REALLOC, start, start + n);
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
public static Instruction stackFree(int n) {
|
public static Instruction stackFree(int n) {
|
||||||
return new Instruction(Type.STACK_FREE, n);
|
return new Instruction(Type.STACK_FREE, n);
|
||||||
}
|
}
|
||||||
|
@ -106,11 +106,12 @@ public final class CompileResult {
|
|||||||
|
|
||||||
for (var suppl : instructions) {
|
for (var suppl : instructions) {
|
||||||
instrRes[i] = suppl.apply(i);
|
instrRes[i] = suppl.apply(i);
|
||||||
|
// System.out.println(instrRes[i]);
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return new FunctionBody(
|
return new FunctionBody(
|
||||||
scope.localsCount() + scope.allocCount(), scope.capturesCount(),
|
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
|
||||||
length, instrRes, builtChildren
|
length, instrRes, builtChildren
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -25,7 +25,7 @@ public class CompoundNode extends Node {
|
|||||||
|
|
||||||
var subtarget = hasScope ? target.subtarget() : target;
|
var subtarget = hasScope ? target.subtarget() : target;
|
||||||
if (hasScope) {
|
if (hasScope) {
|
||||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||||
subtarget.scope.singleEntry = singleEntry;
|
subtarget.scope.singleEntry = singleEntry;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -45,10 +45,7 @@ public class CompoundNode extends Node {
|
|||||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hasScope) {
|
if (hasScope) subtarget.scope.end();
|
||||||
subtarget.scope.end();
|
|
||||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
target.add(Instruction.pushUndefined());
|
target.add(Instruction.pushUndefined());
|
||||||
|
@ -31,13 +31,6 @@ public abstract class FunctionNode extends Node {
|
|||||||
.remove(LabelContext.CONTINUE_CTX);
|
.remove(LabelContext.CONTINUE_CTX);
|
||||||
|
|
||||||
return new CompileResult(env, scope, params.params.size(), target -> {
|
return new CompileResult(env, scope, params.params.size(), target -> {
|
||||||
// if (params.params.size() > 0) target.add(Instruction.loadArgs(true));
|
|
||||||
|
|
||||||
// if (hasArgs) {
|
|
||||||
// var argsVar = scope.defineStrict(new Variable("arguments", true), loc());
|
|
||||||
// target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (params.params.size() > 0) {
|
if (params.params.size() > 0) {
|
||||||
target.add(Instruction.loadArgs(true));
|
target.add(Instruction.loadArgs(true));
|
||||||
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1));
|
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1));
|
||||||
@ -65,7 +58,7 @@ public abstract class FunctionNode extends Node {
|
|||||||
end.set(target.size());
|
end.set(target.size());
|
||||||
}
|
}
|
||||||
|
|
||||||
target.add(_i -> Instruction.storeVar(varI.index()));
|
target.add(_i -> varI.index().toSet(false));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -73,14 +66,14 @@ public abstract class FunctionNode extends Node {
|
|||||||
if (scope.has(params.restName, false)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
|
if (scope.has(params.restName, false)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
|
||||||
var restVar = scope.define(new Variable(params.restName, false), params.restLocation);
|
var restVar = scope.define(new Variable(params.restName, false), params.restLocation);
|
||||||
target.add(Instruction.loadRestArgs(params.params.size()));
|
target.add(Instruction.loadRestArgs(params.params.size()));
|
||||||
target.add(_i -> Instruction.storeVar(restVar.index()));
|
target.add(_i -> restVar.index().toSet(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (selfName != null && !scope.has(name, false)) {
|
if (selfName != null && !scope.has(name, false)) {
|
||||||
var i = scope.defineSpecial(new Variable(selfName, true), end);
|
var i = scope.defineSpecial(new Variable(selfName, true), end);
|
||||||
|
|
||||||
target.add(Instruction.loadCallee());
|
target.add(Instruction.loadCallee());
|
||||||
target.add(_i -> Instruction.storeVar(i.index(), false));
|
target.add(_i -> i.index().toSet(false));
|
||||||
}
|
}
|
||||||
|
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
|
@ -311,6 +311,7 @@ public final class JavaScript {
|
|||||||
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
|
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
|
||||||
|
|
||||||
i += res.n;
|
i += res.n;
|
||||||
|
i += Parsing.skipEmpty(src, i);
|
||||||
|
|
||||||
list.add(res.result);
|
list.add(res.result);
|
||||||
}
|
}
|
||||||
|
@ -26,7 +26,7 @@ public class ForNode extends Node {
|
|||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
var subtarget = target.subtarget();
|
var subtarget = target.subtarget();
|
||||||
subtarget.scope.singleEntry = false;
|
subtarget.scope.singleEntry = false;
|
||||||
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||||
|
|
||||||
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
declaration.compile(subtarget, false, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
@ -40,7 +40,7 @@ public class ForNode extends Node {
|
|||||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||||
LabelContext.popLoop(subtarget.env, label);
|
LabelContext.popLoop(subtarget.env, label);
|
||||||
|
|
||||||
subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.allocCount()));
|
subtarget.add(_i -> Instruction.stackRealloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||||
|
|
||||||
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER);
|
||||||
int endI = subtarget.size();
|
int endI = subtarget.size();
|
||||||
@ -52,7 +52,6 @@ public class ForNode extends Node {
|
|||||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
if (pollute) subtarget.add(Instruction.pushUndefined());
|
||||||
|
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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) {
|
||||||
|
@ -44,7 +44,7 @@ public class SwitchNode extends Node {
|
|||||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
|
|
||||||
var subtarget = target.subtarget();
|
var subtarget = target.subtarget();
|
||||||
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.allocCount()));
|
subtarget.add(_i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||||
|
|
||||||
// TODO: create a jump map
|
// TODO: create a jump map
|
||||||
for (var ccase : cases) {
|
for (var ccase : cases) {
|
||||||
@ -65,7 +65,6 @@ public class SwitchNode extends Node {
|
|||||||
LabelContext.getBreak(target.env).pop(label);
|
LabelContext.getBreak(target.env).pop(label);
|
||||||
|
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
|
||||||
|
|
||||||
int endI = subtarget.size();
|
int endI = subtarget.size();
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
|
@ -23,8 +23,8 @@ public class TryNode extends Node {
|
|||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
tryBody.resolve(target);
|
tryBody.resolve(target);
|
||||||
catchBody.resolve(target);
|
if (catchBody != null) catchBody.resolve(target);
|
||||||
finallyBody.resolve(target);
|
if (finallyBody != null) finallyBody.resolve(target);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
||||||
@ -43,8 +43,14 @@ public class TryNode extends Node {
|
|||||||
|
|
||||||
if (captureName != null) {
|
if (captureName != null) {
|
||||||
var subtarget = target.subtarget();
|
var subtarget = target.subtarget();
|
||||||
subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
subtarget.add(i -> Instruction.stackAlloc(subtarget.scope.capturablesOffset(), subtarget.scope.allocCount()));
|
||||||
|
subtarget.scope.singleEntry = true;
|
||||||
|
|
||||||
|
var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
||||||
|
subtarget.add(Instruction.loadError());
|
||||||
|
subtarget.add(_i -> catchVar.index().toSet(false));
|
||||||
catchBody.compile(subtarget, false);
|
catchBody.compile(subtarget, false);
|
||||||
|
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
}
|
}
|
||||||
else catchBody.compile(target, false);
|
else catchBody.compile(target, false);
|
||||||
|
@ -4,65 +4,56 @@ import java.util.HashMap;
|
|||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
|
||||||
|
|
||||||
public class FunctionScope extends Scope {
|
public class FunctionScope extends Scope {
|
||||||
private final VariableList captures = new VariableList().setIndexMap(v -> ~v);
|
private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES);
|
||||||
private final VariableList specials = new VariableList();
|
|
||||||
private final VariableList locals = new VariableList(specials);
|
private final HashMap<String, Variable> specialVarMap = new HashMap<>();
|
||||||
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
private final HashMap<String, Variable> functionVarMap = new HashMap<>();
|
||||||
|
private final HashMap<String, Variable> capturesMap = new HashMap<>();
|
||||||
private final HashSet<String> blacklistNames = new HashSet<>();
|
private final HashSet<String> blacklistNames = new HashSet<>();
|
||||||
|
|
||||||
|
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||||
|
|
||||||
private final Scope captureParent;
|
private final Scope captureParent;
|
||||||
|
|
||||||
public final boolean passtrough;
|
public final boolean passtrough;
|
||||||
|
|
||||||
private void removeCapture(String name) {
|
|
||||||
var res = captures.remove(name);
|
|
||||||
if (res != null) {
|
|
||||||
childToParent.remove(res);
|
|
||||||
res.setIndexSupplier(() -> { throw new SyntaxException(null, res.name + " has been shadowed"); });
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public Variable define(Variable var, Location loc) {
|
@Override public Variable define(Variable var, Location loc) {
|
||||||
checkNotEnded();
|
checkNotEnded();
|
||||||
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
if (passtrough) {
|
if (passtrough) {
|
||||||
blacklistNames.add(var.name);
|
blacklistNames.add(var.name);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
removeCapture(var.name);
|
functionVarMap.put(var.name, var);
|
||||||
return locals.add(var);
|
return variables.add(var);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@Override public Variable defineStrict(Variable var, Location loc) {
|
@Override public Variable defineStrict(Variable var, Location loc) {
|
||||||
checkNotEnded();
|
checkNotEnded();
|
||||||
if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (functionVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
var res = super.defineStrict(var, loc);
|
return super.defineStrict(var, loc);
|
||||||
removeCapture(var.name);
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
public Variable defineSpecial(Variable var, Location loc) {
|
public Variable defineSpecial(Variable var, Location loc) {
|
||||||
return specials.add(var);
|
checkNotEnded();
|
||||||
}
|
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
@Override public boolean flattenVariable(Variable variable, boolean capturable) {
|
specialVarMap.put(var.name, var);
|
||||||
// if (!ended()) throw new IllegalStateException("Tried to flatten a variable before the scope has ended");
|
return variables.add(var);
|
||||||
this.locals.overlay(variable);
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Variable get(String name, boolean capture) {
|
@Override public Variable get(String name, boolean capture) {
|
||||||
var superRes = super.get(name, capture);
|
var superRes = super.get(name, capture);
|
||||||
if (superRes != null) return superRes;
|
if (superRes != null) return superRes;
|
||||||
|
|
||||||
if (specials.has(name)) return addCaptured(specials.get(name), capture);
|
if (specialVarMap.containsKey(name)) return addCaptured(specialVarMap.get(name), capture);
|
||||||
if (locals.has(name)) return addCaptured(locals.get(name), capture);
|
if (functionVarMap.containsKey(name)) return addCaptured(functionVarMap.get(name), capture);
|
||||||
if (captures.has(name)) return addCaptured(captures.get(name), capture);
|
if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture);
|
||||||
|
|
||||||
if (captureParent == null) return null;
|
if (captureParent == null) return null;
|
||||||
|
|
||||||
@ -70,47 +61,32 @@ public class FunctionScope extends Scope {
|
|||||||
if (parentVar == null) return null;
|
if (parentVar == null) return null;
|
||||||
|
|
||||||
var childVar = captures.add(parentVar.clone());
|
var childVar = captures.add(parentVar.clone());
|
||||||
|
capturesMap.put(childVar.name, childVar);
|
||||||
childToParent.put(childVar, parentVar);
|
childToParent.put(childVar, parentVar);
|
||||||
|
|
||||||
return childVar;
|
return childVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean has(String name, boolean capture) {
|
@Override public boolean has(String name, boolean capture) {
|
||||||
if (specials.has(name)) return true;
|
if (functionVarMap.containsKey(name)) return true;
|
||||||
if (locals.has(name)) return true;
|
if (specialVarMap.containsKey(name)) return true;
|
||||||
|
|
||||||
if (capture) {
|
if (capture) {
|
||||||
if (captures.has(name)) return true;
|
if (capturesMap.containsKey(name)) return true;
|
||||||
if (captureParent != null) return captureParent.has(name, true);
|
if (captureParent != null) return captureParent.has(name, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean finish() {
|
@Override protected void onFinish() {
|
||||||
if (!super.finish()) return false;
|
|
||||||
|
|
||||||
captures.freeze();
|
captures.freeze();
|
||||||
locals.freeze();
|
super.onFinish();
|
||||||
specials.freeze();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int allocCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@Override public int capturesCount() {
|
@Override public int capturesCount() {
|
||||||
return captures.size();
|
return captures.size();
|
||||||
}
|
}
|
||||||
@Override public int localsCount() {
|
|
||||||
return locals.size() + specials.size() + super.allocCount();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int offset() {
|
|
||||||
return specials.size() + locals.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public int[] getCaptureIndices() {
|
public int[] getCaptureIndices() {
|
||||||
var res = new int[captures.size()];
|
var res = new int[captures.size()];
|
||||||
@ -118,7 +94,7 @@ public class FunctionScope extends Scope {
|
|||||||
|
|
||||||
for (var el : captures.all()) {
|
for (var el : captures.all()) {
|
||||||
assert childToParent.containsKey(el);
|
assert childToParent.containsKey(el);
|
||||||
res[i] = childToParent.get(el).index();
|
res[i] = childToParent.get(el).index().toCaptureIndex();
|
||||||
i++;
|
i++;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -130,10 +106,12 @@ public class FunctionScope extends Scope {
|
|||||||
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
||||||
this.captureParent = parent;
|
this.captureParent = parent;
|
||||||
this.passtrough = false;
|
this.passtrough = false;
|
||||||
|
this.singleEntry = false;
|
||||||
}
|
}
|
||||||
public FunctionScope(boolean passtrough) {
|
public FunctionScope(boolean passtrough) {
|
||||||
super();
|
super();
|
||||||
this.captureParent = null;
|
this.captureParent = null;
|
||||||
this.passtrough = passtrough;
|
this.passtrough = passtrough;
|
||||||
|
this.singleEntry = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,23 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
import java.util.HashSet;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||||
|
|
||||||
public class Scope {
|
public class Scope {
|
||||||
protected final VariableList variables = new VariableList(this::parentOffset);
|
protected final HashMap<String, Variable> strictVarMap = new HashMap<>();
|
||||||
|
|
||||||
|
protected final VariableList variables = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset);
|
||||||
|
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset);
|
||||||
|
|
||||||
private boolean ended = false;
|
private boolean ended = false;
|
||||||
private boolean finished = false;
|
private boolean finished = false;
|
||||||
private Scope child;
|
private Scope child;
|
||||||
private List<Scope> prevChildren = new LinkedList<>();
|
private LinkedList<Scope> children = new LinkedList<>();
|
||||||
|
|
||||||
public final Scope parent;
|
public final Scope parent;
|
||||||
public final HashSet<Variable> captured = new HashSet<>();
|
|
||||||
|
|
||||||
protected final Variable addCaptured(Variable var, boolean captured) {
|
|
||||||
if (captured) this.captured.add(var);
|
|
||||||
return var;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wether or not the scope is going to be entered multiple times.
|
* Wether or not the scope is going to be entered multiple times.
|
||||||
@ -29,11 +25,31 @@ public class Scope {
|
|||||||
*/
|
*/
|
||||||
public boolean singleEntry = true;
|
public boolean singleEntry = true;
|
||||||
|
|
||||||
private final int parentOffset() {
|
|
||||||
if (parent != null) return parent.offset();
|
protected void transferCaptured(Variable var) {
|
||||||
else return 0;
|
if (!singleEntry) {
|
||||||
|
this.captured.add(var);
|
||||||
|
}
|
||||||
|
else if (parent != null) {
|
||||||
|
parent.transferCaptured(var);
|
||||||
|
}
|
||||||
|
else throw new IllegalStateException("Couldn't transfer captured variable");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected final Variable addCaptured(Variable var, boolean captured) {
|
||||||
|
if (captured) transferCaptured(var);
|
||||||
|
return var;
|
||||||
|
}
|
||||||
|
|
||||||
|
// private final int parentVarOffset() {
|
||||||
|
// if (parent != null) return parent.variableOffset();
|
||||||
|
// else return 0;
|
||||||
|
// }
|
||||||
|
// private final int parentCapOffset() {
|
||||||
|
// if (parent != null) return parent.capturedOffset();
|
||||||
|
// else return localsCount();
|
||||||
|
// }
|
||||||
|
|
||||||
protected final SyntaxException alreadyDefinedErr(Location loc, String name) {
|
protected final SyntaxException alreadyDefinedErr(Location loc, String name) {
|
||||||
return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name));
|
return new SyntaxException(loc, String.format("Identifier '%s' has already been declared", name));
|
||||||
}
|
}
|
||||||
@ -54,7 +70,7 @@ public class Scope {
|
|||||||
*/
|
*/
|
||||||
public Variable define(Variable var, Location loc) {
|
public Variable define(Variable var, Location loc) {
|
||||||
checkNotEnded();
|
checkNotEnded();
|
||||||
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
if (parent != null) return parent.define(var, loc);
|
if (parent != null) return parent.define(var, loc);
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -69,10 +85,10 @@ public class Scope {
|
|||||||
*/
|
*/
|
||||||
public Variable defineStrict(Variable var, Location loc) {
|
public Variable defineStrict(Variable var, Location loc) {
|
||||||
checkNotEnded();
|
checkNotEnded();
|
||||||
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
variables.add(var);
|
strictVarMap.put(var.name, var);
|
||||||
return var.setIndexSupplier(() -> variables.indexOfKey(var.name));
|
return variables.add(var);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Gets the index supplier of the given variable name, or null if it is a global
|
* Gets the index supplier of the given variable name, or null if it is a global
|
||||||
@ -80,7 +96,8 @@ public class Scope {
|
|||||||
* @param capture If true, the variable is being captured by a function
|
* @param capture If true, the variable is being captured by a function
|
||||||
*/
|
*/
|
||||||
public Variable get(String name, boolean capture) {
|
public Variable get(String name, boolean capture) {
|
||||||
var res = variables.get(name);
|
var res = strictVarMap.get(name);
|
||||||
|
|
||||||
if (res != null) return addCaptured(res, capture);
|
if (res != null) return addCaptured(res, capture);
|
||||||
if (parent != null) return parent.get(name, capture);
|
if (parent != null) return parent.get(name, capture);
|
||||||
|
|
||||||
@ -92,7 +109,7 @@ public class Scope {
|
|||||||
* @param capture If true, will check beyond this function's scope
|
* @param capture If true, will check beyond this function's scope
|
||||||
*/
|
*/
|
||||||
public boolean has(String name, boolean capture) {
|
public boolean has(String name, boolean capture) {
|
||||||
if (variables.has(name)) return true;
|
if (strictVarMap.containsKey(name)) return true;
|
||||||
if (parent != null) return parent.has(name, capture);
|
if (parent != null) return parent.has(name, capture);
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
@ -100,31 +117,50 @@ public class Scope {
|
|||||||
/**
|
/**
|
||||||
* Gets the index offset from this scope to its children
|
* Gets the index offset from this scope to its children
|
||||||
*/
|
*/
|
||||||
public int offset() {
|
public final int variableOffset() {
|
||||||
if (parent != null) return parent.offset() + variables.size();
|
var res = 0;
|
||||||
else return variables.size();
|
|
||||||
|
for (var curr = parent; curr != null; curr = curr.parent) {
|
||||||
|
res += parent.variables.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
|
||||||
|
// if (parent != null) return parent.variableOffset() + variables.size();
|
||||||
|
// else return variables.size();
|
||||||
|
}
|
||||||
|
public final int capturablesOffset() {
|
||||||
|
var res = 0;
|
||||||
|
|
||||||
|
for (var curr = this; curr != null; curr = curr.parent) {
|
||||||
|
if (curr != this) res += parent.captured.size();
|
||||||
|
if (curr.parent == null) res += curr.localsCount();
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
// if (parent != null) return parent.capturedOffset() + captured.size();
|
||||||
|
// else return localsCount() + captured.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public int localsCount() {
|
||||||
* Adds this variable to the current function's locals record. Capturable indicates whether or not the variable
|
var res = 0;
|
||||||
* should still be capturable, or be put in an array (still not implemented)
|
for (var child : children) {
|
||||||
*
|
var childN = child.localsCount();
|
||||||
* @return Whether or not the request was actually fuliflled
|
if (res < childN) res = childN;
|
||||||
*/
|
|
||||||
public boolean flattenVariable(Variable variable, boolean capturable) {
|
|
||||||
if (singleEntry || !capturable) {
|
|
||||||
if (parent == null) return false;
|
|
||||||
return parent.flattenVariable(variable, capturable);
|
|
||||||
}
|
}
|
||||||
else {
|
|
||||||
variables.overlay(variable);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public int localsCount() { return 0; }
|
return res + variables.size();
|
||||||
|
}
|
||||||
public int capturesCount() { return 0; }
|
public int capturesCount() { return 0; }
|
||||||
public int allocCount() { return variables.size(); }
|
public int allocCount() {
|
||||||
|
var res = captured.size();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
public int capturablesCount() {
|
||||||
|
var res = captured.size();
|
||||||
|
for (var child : children) res += child.capturablesCount();
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ends this scope. This will make it possible for another child to take its place
|
* Ends this scope. This will make it possible for another child to take its place
|
||||||
@ -142,34 +178,23 @@ public class Scope {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
protected void onFinish() {
|
||||||
|
this.variables.freeze();
|
||||||
|
this.captured.freeze();
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Finalizes this scope. The scope will become immutable after this call
|
* Finalizes this scope. The scope will become immutable after this call
|
||||||
* @return
|
* @return
|
||||||
*/
|
*/
|
||||||
public boolean finish() {
|
public final boolean finish() {
|
||||||
if (finished) return false;
|
if (finished) return false;
|
||||||
|
|
||||||
if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished");
|
if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished");
|
||||||
|
this.onFinish();
|
||||||
|
|
||||||
for (var child : prevChildren) child.finish();
|
for (var child : children) child.finish();
|
||||||
|
|
||||||
var captured = new HashSet<Variable>();
|
|
||||||
var normal = new HashSet<Variable>();
|
|
||||||
|
|
||||||
for (var v : variables.all()) {
|
|
||||||
if (this.captured.contains(v)) {
|
|
||||||
if (singleEntry) captured.add(v);
|
|
||||||
}
|
|
||||||
else normal.add(v);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var v : captured) variables.remove(v);
|
|
||||||
for (var v : normal) variables.remove(v);
|
|
||||||
|
|
||||||
for (var v : captured) flattenVariable(v, true);
|
|
||||||
for (var v : normal) flattenVariable(v, false);
|
|
||||||
|
|
||||||
|
|
||||||
this.variables.freeze();
|
|
||||||
this.finished = true;
|
this.finished = true;
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
@ -190,7 +215,7 @@ public class Scope {
|
|||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.parent.child = this;
|
this.parent.child = this;
|
||||||
this.parent.prevChildren.add(this);
|
this.parent.children.add(this);
|
||||||
}
|
}
|
||||||
else this.parent = null;
|
else this.parent = null;
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,28 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.Supplier;
|
||||||
|
|
||||||
public final class Variable {
|
public final class Variable {
|
||||||
private IntSupplier indexSupplier;
|
private Supplier<VariableIndex> indexSupplier;
|
||||||
private boolean frozen;
|
private boolean frozen;
|
||||||
|
|
||||||
public final boolean readonly;
|
public final boolean readonly;
|
||||||
public final String name;
|
public final String name;
|
||||||
|
|
||||||
public final int index() {
|
public final VariableIndex index() {
|
||||||
if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized");
|
if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized");
|
||||||
return indexSupplier.getAsInt();
|
return indexSupplier.get();
|
||||||
}
|
}
|
||||||
|
|
||||||
public final void freeze() {
|
public final void freeze() {
|
||||||
this.frozen = true;
|
this.frozen = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Variable setIndexSupplier(IntSupplier index) {
|
public final Variable setIndexSupplier(Supplier<VariableIndex> index) {
|
||||||
this.indexSupplier = index;
|
this.indexSupplier = index;
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
public final IntSupplier indexSupplier() {
|
public final Supplier<VariableIndex> indexSupplier() {
|
||||||
return indexSupplier;
|
return indexSupplier;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -35,7 +35,7 @@ public final class Variable {
|
|||||||
this.readonly = readonly;
|
this.readonly = readonly;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Variable of(String name, boolean readonly, int i) {
|
public static Variable of(String name, boolean readonly, VariableIndex index) {
|
||||||
return new Variable(name, readonly).setIndexSupplier(() -> i);
|
return new Variable(name, readonly).setIndexSupplier(() -> index);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,44 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
|
|
||||||
|
public final class VariableIndex {
|
||||||
|
public static enum IndexType {
|
||||||
|
LOCALS,
|
||||||
|
CAPTURABLES,
|
||||||
|
CAPTURES,
|
||||||
|
}
|
||||||
|
|
||||||
|
public final VariableIndex.IndexType type;
|
||||||
|
public final int index;
|
||||||
|
|
||||||
|
public final int toCaptureIndex() {
|
||||||
|
switch (type) {
|
||||||
|
case CAPTURES: return ~index;
|
||||||
|
case CAPTURABLES: return index;
|
||||||
|
default: throw new UnsupportedOperationException("Index type " + type + " may not be captured");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Instruction toGet() {
|
||||||
|
switch (type) {
|
||||||
|
case CAPTURES: return Instruction.loadVar(~index);
|
||||||
|
case CAPTURABLES: return Instruction.loadVar(index);
|
||||||
|
case LOCALS: return Instruction.loadVar(index);
|
||||||
|
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
public final Instruction toSet(boolean keep) {
|
||||||
|
switch (type) {
|
||||||
|
case CAPTURES: return Instruction.storeVar(~index, keep);
|
||||||
|
case CAPTURABLES: return Instruction.storeVar(index, keep);
|
||||||
|
case LOCALS: return Instruction.storeVar(index, keep);
|
||||||
|
default: throw new UnsupportedOperationException("Unknown index type " + type);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public VariableIndex(VariableIndex.IndexType type, int index) {
|
||||||
|
this.type = type;
|
||||||
|
this.index = index;
|
||||||
|
}
|
||||||
|
}
|
@ -4,27 +4,22 @@ import java.util.ArrayList;
|
|||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.Iterator;
|
import java.util.Iterator;
|
||||||
import java.util.function.IntSupplier;
|
import java.util.function.IntSupplier;
|
||||||
import java.util.function.IntUnaryOperator;
|
import java.util.function.Supplier;
|
||||||
import java.util.stream.StreamSupport;
|
|
||||||
|
|
||||||
public final class VariableList {
|
public final class VariableList {
|
||||||
private final class Node implements IntSupplier {
|
private final class VariableNode implements Supplier<VariableIndex> {
|
||||||
public Variable var;
|
public Variable var;
|
||||||
public Node next;
|
public VariableNode next;
|
||||||
public Node prev;
|
public VariableNode prev;
|
||||||
public boolean frozen;
|
public boolean frozen;
|
||||||
public int index;
|
public int index;
|
||||||
|
|
||||||
@Override public int getAsInt() {
|
public VariableList list() { return VariableList.this; }
|
||||||
|
|
||||||
|
@Override public VariableIndex get() {
|
||||||
if (frozen) {
|
if (frozen) {
|
||||||
if (offset == null) {
|
if (offset == null) return new VariableIndex(indexType, index);
|
||||||
return indexConverter == null ? index : indexConverter.applyAsInt(index);
|
else new VariableIndex(indexType, index + offset.getAsInt());
|
||||||
}
|
|
||||||
else {
|
|
||||||
return indexConverter == null ?
|
|
||||||
index + offset.getAsInt() :
|
|
||||||
indexConverter.applyAsInt(index + offset.getAsInt());
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var res = 0;
|
var res = 0;
|
||||||
@ -34,7 +29,7 @@ public final class VariableList {
|
|||||||
res++;
|
res++;
|
||||||
}
|
}
|
||||||
|
|
||||||
return indexConverter == null ? res : indexConverter.applyAsInt(res);
|
return new VariableIndex(indexType, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void freeze() {
|
public void freeze() {
|
||||||
@ -50,26 +45,26 @@ public final class VariableList {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Node(Variable var, Node next, Node prev) {
|
public VariableNode(Variable var, VariableNode next, VariableNode prev) {
|
||||||
this.var = var;
|
this.var = var;
|
||||||
this.next = next;
|
this.next = next;
|
||||||
this.prev = prev;
|
this.prev = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Node first, last;
|
private VariableNode first, last;
|
||||||
|
|
||||||
private final HashMap<String, Node> map = new HashMap<>();
|
private ArrayList<VariableNode> frozenList = null;
|
||||||
private ArrayList<Node> frozenList = null;
|
private HashMap<Variable, VariableNode> varMap = new HashMap<>();
|
||||||
private HashMap<Variable, Node> varMap = new HashMap<>();
|
|
||||||
|
|
||||||
private final IntSupplier offset;
|
private final IntSupplier offset;
|
||||||
private IntUnaryOperator indexConverter = null;
|
|
||||||
|
public final VariableIndex.IndexType indexType;
|
||||||
|
|
||||||
public boolean frozen() {
|
public boolean frozen() {
|
||||||
if (frozenList != null) {
|
if (frozenList != null) {
|
||||||
assert frozenList != null;
|
assert frozenList != null;
|
||||||
assert map != null;
|
assert varMap == null;
|
||||||
assert first == null;
|
assert first == null;
|
||||||
assert last == null;
|
assert last == null;
|
||||||
|
|
||||||
@ -77,21 +72,20 @@ public final class VariableList {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert frozenList == null;
|
assert frozenList == null;
|
||||||
assert map != null;
|
assert varMap != null;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Variable add(Variable val, boolean overlay) {
|
public Variable add(Variable val) {
|
||||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||||
if (!overlay && map.containsKey(val.name)) {
|
|
||||||
var node = this.map.get(val.name);
|
if (val.indexSupplier() instanceof VariableNode prevNode) {
|
||||||
val.setIndexSupplier(node);
|
prevNode.list().remove(val);
|
||||||
return node.var;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var node = new Node(val, null, last);
|
var node = new VariableNode(val, null, last);
|
||||||
|
|
||||||
if (last != null) {
|
if (last != null) {
|
||||||
assert first != null;
|
assert first != null;
|
||||||
@ -105,28 +99,17 @@ public final class VariableList {
|
|||||||
first = last = node;
|
first = last = node;
|
||||||
}
|
}
|
||||||
|
|
||||||
map.put(val.name, node);
|
|
||||||
varMap.put(val, node);
|
varMap.put(val, node);
|
||||||
val.setIndexSupplier(node);
|
val.setIndexSupplier(node);
|
||||||
|
|
||||||
return val;
|
return val;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Variable add(Variable val) {
|
|
||||||
return this.add(val, false);
|
|
||||||
}
|
|
||||||
public Variable overlay(Variable val) {
|
|
||||||
return this.add(val, true);
|
|
||||||
}
|
|
||||||
public Variable remove(String key) {
|
|
||||||
var res = map.get(key);
|
|
||||||
if (res != null) return remove(res.var);
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
public Variable remove(Variable var) {
|
public Variable remove(Variable var) {
|
||||||
if (var == null) return null;
|
|
||||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||||
|
|
||||||
|
if (var == null) return null;
|
||||||
|
|
||||||
var node = varMap.get(var);
|
var node = varMap.get(var);
|
||||||
if (node == null) return null;
|
if (node == null) return null;
|
||||||
|
|
||||||
@ -151,28 +134,18 @@ public final class VariableList {
|
|||||||
node.next = null;
|
node.next = null;
|
||||||
node.prev = null;
|
node.prev = null;
|
||||||
|
|
||||||
map.remove(node.var.name);
|
|
||||||
varMap.remove(node.var);
|
varMap.remove(node.var);
|
||||||
|
|
||||||
return node.var;
|
return node.var;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Variable get(String name) {
|
public Supplier<VariableIndex> indexer(Variable var) {
|
||||||
var res = map.get(name);
|
return varMap.get(var);
|
||||||
if (res != null) return res.var;
|
|
||||||
else return null;
|
|
||||||
}
|
|
||||||
public int indexOfKey(String name) {
|
|
||||||
return map.get(name).getAsInt();
|
|
||||||
}
|
|
||||||
|
|
||||||
public boolean has(String name) {
|
|
||||||
return this.map.containsKey(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
if (frozen()) return frozenList.size();
|
if (frozen()) return frozenList.size();
|
||||||
else return map.size();
|
else return varMap.size();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void freeze() {
|
public void freeze() {
|
||||||
@ -195,7 +168,7 @@ public final class VariableList {
|
|||||||
public Iterable<Variable> all() {
|
public Iterable<Variable> all() {
|
||||||
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
||||||
else return () -> new Iterator<Variable>() {
|
else return () -> new Iterator<Variable>() {
|
||||||
private Node curr = first;
|
private VariableNode curr = first;
|
||||||
|
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return curr != null;
|
return curr != null;
|
||||||
@ -209,25 +182,21 @@ public final class VariableList {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
public Iterable<String> keys() {
|
|
||||||
return () -> StreamSupport.stream(all().spliterator(), false).map(v -> v.name).iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableList setIndexMap(IntUnaryOperator map) {
|
public VariableList(VariableIndex.IndexType type, IntSupplier offset) {
|
||||||
indexConverter = map;
|
this.indexType = type;
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public VariableList(IntSupplier offset) {
|
|
||||||
this.offset = offset;
|
this.offset = offset;
|
||||||
}
|
}
|
||||||
public VariableList(int offset) {
|
public VariableList(VariableIndex.IndexType type, int offset) {
|
||||||
|
this.indexType = type;
|
||||||
this.offset = () -> offset;
|
this.offset = () -> offset;
|
||||||
}
|
}
|
||||||
public VariableList(VariableList prev) {
|
public VariableList(VariableIndex.IndexType type, VariableList prev) {
|
||||||
|
this.indexType = type;
|
||||||
this.offset = prev::size;
|
this.offset = prev::size;
|
||||||
}
|
}
|
||||||
public VariableList() {
|
public VariableList(VariableIndex.IndexType type) {
|
||||||
|
this.indexType = type;
|
||||||
this.offset = null;
|
this.offset = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -34,9 +34,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
|
|
||||||
if (!pollute) target.add(Instruction.discard());
|
if (!pollute) target.add(Instruction.discard());
|
||||||
}
|
}
|
||||||
else if (pollute) {
|
else if (pollute) target.add(_i -> i.index().toGet());
|
||||||
target.add(_i -> Instruction.loadVar(i.index()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, Supplier<Instruction> onGlobal) {
|
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name, Supplier<Instruction> onGlobal) {
|
||||||
@ -46,7 +44,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
if (target.scope.has(name, false)) return Instruction.throwSyntax(loc, String.format("Cannot access '%s' before initialization", name));
|
||||||
else return onGlobal.get();
|
else return onGlobal.get();
|
||||||
};
|
};
|
||||||
else return _i -> Instruction.loadVar(i.index());
|
else return _i -> i.index().toGet();
|
||||||
}
|
}
|
||||||
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name) {
|
public static IntFunction<Instruction> toGet(CompileResult target, Location loc, String name) {
|
||||||
return toGet(target, loc, name, () -> Instruction.globGet(name));
|
return toGet(target, loc, name, () -> Instruction.globGet(name));
|
||||||
@ -61,7 +59,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
else return Instruction.globSet(name, keep, define);
|
else return Instruction.globSet(name, keep, define);
|
||||||
};
|
};
|
||||||
else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable"));
|
else if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable"));
|
||||||
else return _i -> Instruction.storeVar(i.index(), keep);
|
else return _i -> i.index().toSet(keep);
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableNode(Location loc, String name) {
|
public VariableNode(Location loc, String name) {
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
package me.topchetoeu.jscript.runtime;
|
package me.topchetoeu.jscript.runtime;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Arrays;
|
||||||
import java.util.LinkedHashMap;
|
import java.util.LinkedHashMap;
|
||||||
import java.util.List;
|
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.Stack;
|
import java.util.Stack;
|
||||||
|
|
||||||
@ -18,7 +17,6 @@ import me.topchetoeu.jscript.runtime.values.Value;
|
|||||||
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
import me.topchetoeu.jscript.runtime.values.Member.FieldMember;
|
||||||
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
|
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
|
||||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||||
import me.topchetoeu.jscript.runtime.values.objects.ScopeValue;
|
|
||||||
|
|
||||||
public final class Frame {
|
public final class Frame {
|
||||||
public static final Key<Frame> KEY = Key.of();
|
public static final Key<Frame> KEY = Key.of();
|
||||||
@ -100,7 +98,8 @@ public final class Frame {
|
|||||||
* A list of one-element arrays of values. This is so that we can pass captures to other functions
|
* A list of one-element arrays of values. This is so that we can pass captures to other functions
|
||||||
*/
|
*/
|
||||||
public final Value[][] captures;
|
public final Value[][] captures;
|
||||||
public final List<Value[]> locals = new ArrayList<>();
|
public final Value[] locals;
|
||||||
|
public final Value[][] capturables;
|
||||||
public final Value argsVal;
|
public final Value argsVal;
|
||||||
public Value self;
|
public Value self;
|
||||||
public Value fakeArgs;
|
public Value fakeArgs;
|
||||||
@ -111,9 +110,20 @@ public final class Frame {
|
|||||||
public final Environment env;
|
public final Environment env;
|
||||||
private final DebugContext dbg;
|
private final DebugContext dbg;
|
||||||
|
|
||||||
public Value[] getVar(int i) {
|
public Value getVar(int i) {
|
||||||
|
if (i < 0) return captures[~i][0];
|
||||||
|
else if (i < locals.length) return locals[i];
|
||||||
|
else return capturables[i - locals.length][0];
|
||||||
|
}
|
||||||
|
public Value setVar(int i, Value val) {
|
||||||
|
if (i < 0) return captures[~i][0] = val;
|
||||||
|
else if (i < locals.length) return locals[i] = val;
|
||||||
|
else return capturables[i - locals.length][0] = val;
|
||||||
|
}
|
||||||
|
public Value[] captureVar(int i) {
|
||||||
if (i < 0) return captures[~i];
|
if (i < 0) return captures[~i];
|
||||||
else return locals.get(i);
|
if (i >= locals.length) return capturables[i - locals.length];
|
||||||
|
else throw new RuntimeException("Illegal capture");
|
||||||
}
|
}
|
||||||
|
|
||||||
public Value[] stack = new Value[32];
|
public Value[] stack = new Value[32];
|
||||||
@ -216,12 +226,13 @@ public final class Frame {
|
|||||||
if (newCtx != tryCtx) {
|
if (newCtx != tryCtx) {
|
||||||
switch (newCtx.state) {
|
switch (newCtx.state) {
|
||||||
case CATCH:
|
case CATCH:
|
||||||
if (tryCtx.state != TryState.CATCH) locals.add(new Value[] { error.value });
|
// TODO: may cause problems
|
||||||
|
// if (tryCtx.state != TryState.CATCH) locals.add(new Value[] { error.value });
|
||||||
codePtr = tryCtx.catchStart;
|
codePtr = tryCtx.catchStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
break;
|
break;
|
||||||
case FINALLY:
|
case FINALLY:
|
||||||
if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
|
// if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
stackPtr = tryCtx.restoreStackPtr;
|
stackPtr = tryCtx.restoreStackPtr;
|
||||||
default:
|
default:
|
||||||
@ -237,7 +248,7 @@ public final class Frame {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
popTryFlag = false;
|
popTryFlag = false;
|
||||||
if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
|
// if (tryCtx.state == TryState.CATCH) locals.remove(locals.size() - 1);
|
||||||
|
|
||||||
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
if (tryCtx.state != TryState.FINALLY && tryCtx.hasFinally()) {
|
||||||
codePtr = tryCtx.finallyStart;
|
codePtr = tryCtx.finallyStart;
|
||||||
@ -357,18 +368,18 @@ public final class Frame {
|
|||||||
* Gets an object proxy of the capture locals
|
* Gets an object proxy of the capture locals
|
||||||
*/
|
*/
|
||||||
public ObjectValue getCaptureScope() {
|
public ObjectValue getCaptureScope() {
|
||||||
// throw new RuntimeException("Not supported");
|
throw new RuntimeException("Not supported");
|
||||||
|
|
||||||
var names = new String[captures.length];
|
// var names = new String[captures.length];
|
||||||
var map = DebugContext.get(env).getMapOrEmpty(function);
|
// var map = DebugContext.get(env).getMapOrEmpty(function);
|
||||||
|
|
||||||
for (int i = 0; i < captures.length; i++) {
|
// for (int i = 0; i < captures.length; i++) {
|
||||||
var name = "capture_" + (i - 2);
|
// var name = "capture_" + (i - 2);
|
||||||
if (i < map.captureNames.length) name = map.captureNames[i];
|
// if (i < map.captureNames.length) name = map.captureNames[i];
|
||||||
names[i] = name;
|
// names[i] = name;
|
||||||
}
|
// }
|
||||||
|
|
||||||
return new ScopeValue(captures, names);
|
// return new ScopeValue(captures, names);
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Gets an array proxy of the local locals
|
* Gets an array proxy of the local locals
|
||||||
@ -423,8 +434,13 @@ public final class Frame {
|
|||||||
|
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (i = 0; i < func.body.localsN; i++) {
|
this.locals = new Value[func.body.localsN];
|
||||||
this.locals.add(new Value[] { Value.UNDEFINED });
|
Arrays.fill(locals, Value.UNDEFINED);
|
||||||
|
|
||||||
|
this.capturables = new Value[func.body.capturablesN][1];
|
||||||
|
|
||||||
|
for (i = 0; i < func.body.capturablesN; i++) {
|
||||||
|
this.capturables[i][0] = Value.UNDEFINED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -140,7 +140,7 @@ public class InstructionRunner {
|
|||||||
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
|
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
|
||||||
int i = instr.get(0);
|
int i = instr.get(0);
|
||||||
|
|
||||||
frame.push(frame.getVar(i)[0]);
|
frame.push(frame.getVar(i));
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -179,7 +179,7 @@ public class InstructionRunner {
|
|||||||
var captures = new Value[instr.params.length - 5][];
|
var captures = new Value[instr.params.length - 5][];
|
||||||
|
|
||||||
for (var i = 5; i < instr.params.length; i++) {
|
for (var i = 5; i < instr.params.length; i++) {
|
||||||
captures[i - 5] = frame.getVar(instr.get(i));
|
captures[i - 5] = frame.captureVar(instr.get(i));
|
||||||
}
|
}
|
||||||
|
|
||||||
var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
|
var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
|
||||||
@ -278,7 +278,7 @@ public class InstructionRunner {
|
|||||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||||
int i = instr.get(0);
|
int i = instr.get(0);
|
||||||
|
|
||||||
frame.getVar(i)[0] = val;
|
frame.setVar(i, val);
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
@ -340,8 +340,6 @@ public class InstructionRunner {
|
|||||||
frame.stackPtr -= 1;
|
frame.stackPtr -= 1;
|
||||||
var ptr = frame.stackPtr;
|
var ptr = frame.stackPtr;
|
||||||
|
|
||||||
// for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
|
||||||
|
|
||||||
switch (op) {
|
switch (op) {
|
||||||
case ADD:
|
case ADD:
|
||||||
res = Value.add(env, stack[ptr - 1], stack[ptr]);
|
res = Value.add(env, stack[ptr - 1], stack[ptr]);
|
||||||
@ -500,29 +498,34 @@ public class InstructionRunner {
|
|||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
private static Value execLoadError(Environment env, Instruction instr, Frame frame) {
|
||||||
|
frame.push(frame.tryStack.peek().error.value);
|
||||||
|
frame.codePtr++;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) {
|
private static Value execStackAlloc(Environment env, Instruction instr, Frame frame) {
|
||||||
int n = instr.get(0);
|
int offset = instr.get(0);
|
||||||
|
int n = instr.get(1);
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) frame.locals.add(new Value[] { Value.UNDEFINED });
|
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { Value.UNDEFINED };
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) {
|
private static Value execStackRealloc(Environment env, Instruction instr, Frame frame) {
|
||||||
int n = instr.get(0);
|
int offset = instr.get(0);
|
||||||
|
int n = instr.get(1);
|
||||||
|
|
||||||
for (var i = frame.locals.size() - n; i < frame.locals.size(); i++) frame.locals.set(i, new Value[] { frame.locals.get(i)[0] });
|
for (var i = offset; i < n; i++) frame.capturables[i] = new Value[] { frame.capturables[i][0] };
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
private static Value execStackFree(Environment env, Instruction instr, Frame frame) {
|
private static Value execStackFree(Environment env, Instruction instr, Frame frame) {
|
||||||
int n = instr.get(0);
|
// int n = instr.get(0);
|
||||||
|
|
||||||
for (var i = 0; i < n; i++) {
|
// TODO: Remove if safe to do so
|
||||||
frame.locals.remove(frame.locals.size() - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.codePtr++;
|
frame.codePtr++;
|
||||||
return null;
|
return null;
|
||||||
@ -561,6 +564,7 @@ public class InstructionRunner {
|
|||||||
case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame);
|
case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame);
|
||||||
case LOAD_CALLEE: return execLoadCallee(env, instr, frame);
|
case LOAD_CALLEE: return execLoadCallee(env, instr, frame);
|
||||||
case LOAD_THIS: return execLoadThis(env, instr, frame);
|
case LOAD_THIS: return execLoadThis(env, instr, frame);
|
||||||
|
case LOAD_ERROR: return execLoadError(env, instr, frame);
|
||||||
|
|
||||||
case DISCARD: return execDiscard(env, instr, frame);
|
case DISCARD: return execDiscard(env, instr, frame);
|
||||||
case STORE_MEMBER: return execStoreMember(env, instr, frame);
|
case STORE_MEMBER: return execStoreMember(env, instr, frame);
|
||||||
|
@ -257,8 +257,6 @@ const Function = function() {
|
|||||||
parts[parts.length] = String(arguments[arguments.length - 1]);
|
parts[parts.length] = String(arguments[arguments.length - 1]);
|
||||||
parts[parts.length] = "\n}";
|
parts[parts.length] = "\n}";
|
||||||
|
|
||||||
print(parts);
|
|
||||||
|
|
||||||
const res = compile(stringBuild(parts))();
|
const res = compile(stringBuild(parts))();
|
||||||
return res;
|
return res;
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user