Merge pull request #27 from TopchetoEU/TopchetoEU/optimize-var-flatten
Optimize var flattening
This commit is contained in:
commit
b6f04aa177
@ -3,12 +3,13 @@ package me.topchetoeu.jscript.common;
|
||||
public class FunctionBody {
|
||||
public final FunctionBody[] children;
|
||||
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.length = length;
|
||||
this.localsN = localsN;
|
||||
this.capturablesN = capturablesN;
|
||||
this.capturesN = capturesN;
|
||||
this.instructions = instructions;
|
||||
}
|
||||
|
@ -35,20 +35,21 @@ public class Instruction {
|
||||
LOAD_FUNC(0x30),
|
||||
LOAD_ARR(0x31),
|
||||
LOAD_OBJ(0x32),
|
||||
LOAD_GLOB(0x33),
|
||||
LOAD_INTRINSICS(0x34),
|
||||
LOAD_REGEX(0x35),
|
||||
LOAD_REGEX(0x33),
|
||||
|
||||
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_MEMBER(0x41),
|
||||
LOAD_MEMBER_INT(0x42),
|
||||
LOAD_MEMBER_STR(0x43),
|
||||
|
||||
LOAD_ARGS(0x44),
|
||||
LOAD_REST_ARGS(0x45),
|
||||
LOAD_CALLEE(0x46),
|
||||
LOAD_THIS(0x47),
|
||||
|
||||
STORE_VAR(0x48),
|
||||
STORE_MEMBER(0x49),
|
||||
STORE_MEMBER_INT(0x4A),
|
||||
@ -368,6 +369,9 @@ public class Instruction {
|
||||
public static Instruction loadIntrinsics(String key) {
|
||||
return new Instruction(Type.LOAD_INTRINSICS, key);
|
||||
}
|
||||
public static Instruction loadError() {
|
||||
return new Instruction(Type.LOAD_ERROR);
|
||||
}
|
||||
public static Instruction loadMember() {
|
||||
return new Instruction(Type.LOAD_MEMBER);
|
||||
}
|
||||
@ -457,12 +461,15 @@ public class Instruction {
|
||||
return new Instruction(Type.OPERATION, op);
|
||||
}
|
||||
|
||||
public static Instruction stackAlloc(int n) {
|
||||
return new Instruction(Type.STACK_ALLOC, n);
|
||||
public static Instruction stackAlloc(int start, int n) {
|
||||
return new Instruction(Type.STACK_ALLOC, start, start + n);
|
||||
}
|
||||
public static Instruction stackRealloc(int n) {
|
||||
return new Instruction(Type.STACK_REALLOC, n);
|
||||
public static Instruction stackRealloc(int start, int n) {
|
||||
return new Instruction(Type.STACK_REALLOC, start, start + n);
|
||||
}
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public static Instruction stackFree(int n) {
|
||||
return new Instruction(Type.STACK_FREE, n);
|
||||
}
|
||||
|
@ -106,11 +106,12 @@ public final class CompileResult {
|
||||
|
||||
for (var suppl : instructions) {
|
||||
instrRes[i] = suppl.apply(i);
|
||||
// System.out.println(instrRes[i]);
|
||||
i++;
|
||||
}
|
||||
|
||||
return new FunctionBody(
|
||||
scope.localsCount() + scope.allocCount(), scope.capturesCount(),
|
||||
scope.localsCount(), scope.capturablesCount(), scope.capturesCount(),
|
||||
length, instrRes, builtChildren
|
||||
);
|
||||
}
|
||||
|
@ -25,7 +25,7 @@ public class CompoundNode extends Node {
|
||||
|
||||
var subtarget = hasScope ? target.subtarget() : target;
|
||||
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;
|
||||
}
|
||||
|
||||
@ -45,10 +45,7 @@ public class CompoundNode extends Node {
|
||||
else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER);
|
||||
}
|
||||
|
||||
if (hasScope) {
|
||||
subtarget.scope.end();
|
||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
}
|
||||
if (hasScope) subtarget.scope.end();
|
||||
|
||||
if (!polluted && pollute) {
|
||||
target.add(Instruction.pushUndefined());
|
||||
|
@ -31,13 +31,6 @@ public abstract class FunctionNode extends Node {
|
||||
.remove(LabelContext.CONTINUE_CTX);
|
||||
|
||||
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) {
|
||||
target.add(Instruction.loadArgs(true));
|
||||
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());
|
||||
}
|
||||
|
||||
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");
|
||||
var restVar = scope.define(new Variable(params.restName, false), params.restLocation);
|
||||
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)) {
|
||||
var i = scope.defineSpecial(new Variable(selfName, true), end);
|
||||
|
||||
target.add(Instruction.loadCallee());
|
||||
target.add(_i -> Instruction.storeVar(i.index(), false));
|
||||
target.add(_i -> i.index().toSet(false));
|
||||
}
|
||||
|
||||
body.resolve(target);
|
||||
|
@ -311,6 +311,7 @@ public final class JavaScript {
|
||||
else if (res.isFailed()) throw new SyntaxException(src.loc(i), "Unexpected syntax");
|
||||
|
||||
i += res.n;
|
||||
i += Parsing.skipEmpty(src, i);
|
||||
|
||||
list.add(res.result);
|
||||
}
|
||||
|
@ -26,7 +26,7 @@ public class ForNode extends Node {
|
||||
@Override public void compile(CompileResult target, boolean pollute) {
|
||||
var subtarget = target.subtarget();
|
||||
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);
|
||||
|
||||
@ -40,7 +40,7 @@ public class ForNode extends Node {
|
||||
CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER);
|
||||
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);
|
||||
int endI = subtarget.size();
|
||||
@ -52,7 +52,6 @@ public class ForNode extends Node {
|
||||
if (pollute) subtarget.add(Instruction.pushUndefined());
|
||||
|
||||
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) {
|
||||
|
@ -44,7 +44,7 @@ public class SwitchNode extends Node {
|
||||
value.compile(target, true, BreakpointType.STEP_OVER);
|
||||
|
||||
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
|
||||
for (var ccase : cases) {
|
||||
@ -65,7 +65,6 @@ public class SwitchNode extends Node {
|
||||
LabelContext.getBreak(target.env).pop(label);
|
||||
|
||||
subtarget.scope.end();
|
||||
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
||||
|
||||
int endI = subtarget.size();
|
||||
end.set(endI);
|
||||
|
@ -23,8 +23,8 @@ public class TryNode extends Node {
|
||||
|
||||
@Override public void resolve(CompileResult target) {
|
||||
tryBody.resolve(target);
|
||||
catchBody.resolve(target);
|
||||
finallyBody.resolve(target);
|
||||
if (catchBody != null) catchBody.resolve(target);
|
||||
if (finallyBody != null) finallyBody.resolve(target);
|
||||
}
|
||||
|
||||
@Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) {
|
||||
@ -43,8 +43,14 @@ public class TryNode extends Node {
|
||||
|
||||
if (captureName != null) {
|
||||
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);
|
||||
|
||||
subtarget.scope.end();
|
||||
}
|
||||
else catchBody.compile(target, false);
|
||||
|
@ -4,65 +4,56 @@ import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
public class FunctionScope extends Scope {
|
||||
private final VariableList captures = new VariableList().setIndexMap(v -> ~v);
|
||||
private final VariableList specials = new VariableList();
|
||||
private final VariableList locals = new VariableList(specials);
|
||||
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||
private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES);
|
||||
|
||||
private final HashMap<String, Variable> specialVarMap = 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 HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||
|
||||
private final Scope captureParent;
|
||||
|
||||
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) {
|
||||
checkNotEnded();
|
||||
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
if (passtrough) {
|
||||
blacklistNames.add(var.name);
|
||||
return null;
|
||||
}
|
||||
|
||||
removeCapture(var.name);
|
||||
return locals.add(var);
|
||||
else {
|
||||
functionVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
}
|
||||
}
|
||||
@Override public Variable defineStrict(Variable var, Location loc) {
|
||||
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);
|
||||
|
||||
var res = super.defineStrict(var, loc);
|
||||
removeCapture(var.name);
|
||||
return res;
|
||||
return super.defineStrict(var, 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) {
|
||||
// if (!ended()) throw new IllegalStateException("Tried to flatten a variable before the scope has ended");
|
||||
this.locals.overlay(variable);
|
||||
return true;
|
||||
specialVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
}
|
||||
|
||||
@Override public Variable get(String name, boolean capture) {
|
||||
var superRes = super.get(name, capture);
|
||||
if (superRes != null) return superRes;
|
||||
|
||||
if (specials.has(name)) return addCaptured(specials.get(name), capture);
|
||||
if (locals.has(name)) return addCaptured(locals.get(name), capture);
|
||||
if (captures.has(name)) return addCaptured(captures.get(name), capture);
|
||||
if (specialVarMap.containsKey(name)) return addCaptured(specialVarMap.get(name), capture);
|
||||
if (functionVarMap.containsKey(name)) return addCaptured(functionVarMap.get(name), capture);
|
||||
if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture);
|
||||
|
||||
if (captureParent == null) return null;
|
||||
|
||||
@ -70,47 +61,32 @@ public class FunctionScope extends Scope {
|
||||
if (parentVar == null) return null;
|
||||
|
||||
var childVar = captures.add(parentVar.clone());
|
||||
|
||||
capturesMap.put(childVar.name, childVar);
|
||||
childToParent.put(childVar, parentVar);
|
||||
|
||||
return childVar;
|
||||
}
|
||||
|
||||
@Override public boolean has(String name, boolean capture) {
|
||||
if (specials.has(name)) return true;
|
||||
if (locals.has(name)) return true;
|
||||
if (functionVarMap.containsKey(name)) return true;
|
||||
if (specialVarMap.containsKey(name)) return true;
|
||||
|
||||
if (capture) {
|
||||
if (captures.has(name)) return true;
|
||||
if (capturesMap.containsKey(name)) return true;
|
||||
if (captureParent != null) return captureParent.has(name, true);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override public boolean finish() {
|
||||
if (!super.finish()) return false;
|
||||
|
||||
@Override protected void onFinish() {
|
||||
captures.freeze();
|
||||
locals.freeze();
|
||||
specials.freeze();
|
||||
|
||||
return true;
|
||||
super.onFinish();
|
||||
}
|
||||
|
||||
@Override public int allocCount() {
|
||||
return 0;
|
||||
}
|
||||
@Override public int capturesCount() {
|
||||
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() {
|
||||
var res = new int[captures.size()];
|
||||
@ -118,7 +94,7 @@ public class FunctionScope extends Scope {
|
||||
|
||||
for (var el : captures.all()) {
|
||||
assert childToParent.containsKey(el);
|
||||
res[i] = childToParent.get(el).index();
|
||||
res[i] = childToParent.get(el).index().toCaptureIndex();
|
||||
i++;
|
||||
}
|
||||
|
||||
@ -130,10 +106,12 @@ public class FunctionScope extends Scope {
|
||||
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
||||
this.captureParent = parent;
|
||||
this.passtrough = false;
|
||||
this.singleEntry = false;
|
||||
}
|
||||
public FunctionScope(boolean passtrough) {
|
||||
super();
|
||||
this.captureParent = null;
|
||||
this.passtrough = passtrough;
|
||||
this.singleEntry = false;
|
||||
}
|
||||
}
|
||||
|
@ -1,27 +1,23 @@
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
|
||||
import me.topchetoeu.jscript.common.parsing.Location;
|
||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||
|
||||
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 finished = false;
|
||||
private Scope child;
|
||||
private List<Scope> prevChildren = new LinkedList<>();
|
||||
private LinkedList<Scope> children = new LinkedList<>();
|
||||
|
||||
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.
|
||||
@ -29,10 +25,30 @@ public class Scope {
|
||||
*/
|
||||
public boolean singleEntry = true;
|
||||
|
||||
private final int parentOffset() {
|
||||
if (parent != null) return parent.offset();
|
||||
else return 0;
|
||||
|
||||
protected void transferCaptured(Variable var) {
|
||||
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) {
|
||||
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) {
|
||||
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);
|
||||
|
||||
return null;
|
||||
@ -69,10 +85,10 @@ public class Scope {
|
||||
*/
|
||||
public Variable defineStrict(Variable var, Location loc) {
|
||||
checkNotEnded();
|
||||
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||
|
||||
variables.add(var);
|
||||
return var.setIndexSupplier(() -> variables.indexOfKey(var.name));
|
||||
strictVarMap.put(var.name, var);
|
||||
return variables.add(var);
|
||||
}
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
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 (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
|
||||
*/
|
||||
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);
|
||||
|
||||
return false;
|
||||
@ -100,31 +117,50 @@ public class Scope {
|
||||
/**
|
||||
* Gets the index offset from this scope to its children
|
||||
*/
|
||||
public int offset() {
|
||||
if (parent != null) return parent.offset() + variables.size();
|
||||
else return variables.size();
|
||||
public final int variableOffset() {
|
||||
var res = 0;
|
||||
|
||||
for (var curr = parent; curr != null; curr = curr.parent) {
|
||||
res += parent.variables.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds this variable to the current function's locals record. Capturable indicates whether or not the variable
|
||||
* should still be capturable, or be put in an array (still not implemented)
|
||||
*
|
||||
* @return Whether or not the request was actually fuliflled
|
||||
*/
|
||||
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;
|
||||
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();
|
||||
}
|
||||
|
||||
public int localsCount() { return 0; }
|
||||
return res;
|
||||
// if (parent != null) return parent.capturedOffset() + captured.size();
|
||||
// else return localsCount() + captured.size();
|
||||
}
|
||||
|
||||
public int localsCount() {
|
||||
var res = 0;
|
||||
for (var child : children) {
|
||||
var childN = child.localsCount();
|
||||
if (res < childN) res = childN;
|
||||
}
|
||||
|
||||
return res + variables.size();
|
||||
}
|
||||
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
|
||||
@ -142,34 +178,23 @@ public class Scope {
|
||||
return true;
|
||||
}
|
||||
|
||||
protected void onFinish() {
|
||||
this.variables.freeze();
|
||||
this.captured.freeze();
|
||||
}
|
||||
|
||||
/**
|
||||
* Finalizes this scope. The scope will become immutable after this call
|
||||
* @return
|
||||
*/
|
||||
public boolean finish() {
|
||||
public final boolean finish() {
|
||||
if (finished) return false;
|
||||
|
||||
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;
|
||||
|
||||
return true;
|
||||
@ -190,7 +215,7 @@ public class Scope {
|
||||
|
||||
this.parent = parent;
|
||||
this.parent.child = this;
|
||||
this.parent.prevChildren.add(this);
|
||||
this.parent.children.add(this);
|
||||
}
|
||||
else this.parent = null;
|
||||
}
|
||||
|
@ -1,28 +1,28 @@
|
||||
package me.topchetoeu.jscript.compilation.scope;
|
||||
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class Variable {
|
||||
private IntSupplier indexSupplier;
|
||||
private Supplier<VariableIndex> indexSupplier;
|
||||
private boolean frozen;
|
||||
|
||||
public final boolean readonly;
|
||||
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");
|
||||
return indexSupplier.getAsInt();
|
||||
return indexSupplier.get();
|
||||
}
|
||||
|
||||
public final void freeze() {
|
||||
this.frozen = true;
|
||||
}
|
||||
|
||||
public final Variable setIndexSupplier(IntSupplier index) {
|
||||
public final Variable setIndexSupplier(Supplier<VariableIndex> index) {
|
||||
this.indexSupplier = index;
|
||||
return this;
|
||||
}
|
||||
public final IntSupplier indexSupplier() {
|
||||
public final Supplier<VariableIndex> indexSupplier() {
|
||||
return indexSupplier;
|
||||
}
|
||||
|
||||
@ -35,7 +35,7 @@ public final class Variable {
|
||||
this.readonly = readonly;
|
||||
}
|
||||
|
||||
public static Variable of(String name, boolean readonly, int i) {
|
||||
return new Variable(name, readonly).setIndexSupplier(() -> i);
|
||||
public static Variable of(String name, boolean readonly, VariableIndex index) {
|
||||
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.Iterator;
|
||||
import java.util.function.IntSupplier;
|
||||
import java.util.function.IntUnaryOperator;
|
||||
import java.util.stream.StreamSupport;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public final class VariableList {
|
||||
private final class Node implements IntSupplier {
|
||||
private final class VariableNode implements Supplier<VariableIndex> {
|
||||
public Variable var;
|
||||
public Node next;
|
||||
public Node prev;
|
||||
public VariableNode next;
|
||||
public VariableNode prev;
|
||||
public boolean frozen;
|
||||
public int index;
|
||||
|
||||
@Override public int getAsInt() {
|
||||
public VariableList list() { return VariableList.this; }
|
||||
|
||||
@Override public VariableIndex get() {
|
||||
if (frozen) {
|
||||
if (offset == null) {
|
||||
return indexConverter == null ? index : indexConverter.applyAsInt(index);
|
||||
}
|
||||
else {
|
||||
return indexConverter == null ?
|
||||
index + offset.getAsInt() :
|
||||
indexConverter.applyAsInt(index + offset.getAsInt());
|
||||
}
|
||||
if (offset == null) return new VariableIndex(indexType, index);
|
||||
else new VariableIndex(indexType, index + offset.getAsInt());
|
||||
}
|
||||
|
||||
var res = 0;
|
||||
@ -34,7 +29,7 @@ public final class VariableList {
|
||||
res++;
|
||||
}
|
||||
|
||||
return indexConverter == null ? res : indexConverter.applyAsInt(res);
|
||||
return new VariableIndex(indexType, res);
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
@ -50,26 +45,26 @@ public final class VariableList {
|
||||
return;
|
||||
}
|
||||
|
||||
public Node(Variable var, Node next, Node prev) {
|
||||
public VariableNode(Variable var, VariableNode next, VariableNode prev) {
|
||||
this.var = var;
|
||||
this.next = next;
|
||||
this.prev = prev;
|
||||
}
|
||||
}
|
||||
|
||||
private Node first, last;
|
||||
private VariableNode first, last;
|
||||
|
||||
private final HashMap<String, Node> map = new HashMap<>();
|
||||
private ArrayList<Node> frozenList = null;
|
||||
private HashMap<Variable, Node> varMap = new HashMap<>();
|
||||
private ArrayList<VariableNode> frozenList = null;
|
||||
private HashMap<Variable, VariableNode> varMap = new HashMap<>();
|
||||
|
||||
private final IntSupplier offset;
|
||||
private IntUnaryOperator indexConverter = null;
|
||||
|
||||
public final VariableIndex.IndexType indexType;
|
||||
|
||||
public boolean frozen() {
|
||||
if (frozenList != null) {
|
||||
assert frozenList != null;
|
||||
assert map != null;
|
||||
assert varMap == null;
|
||||
assert first == null;
|
||||
assert last == null;
|
||||
|
||||
@ -77,21 +72,20 @@ public final class VariableList {
|
||||
}
|
||||
else {
|
||||
assert frozenList == null;
|
||||
assert map != null;
|
||||
assert varMap != null;
|
||||
|
||||
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 (!overlay && map.containsKey(val.name)) {
|
||||
var node = this.map.get(val.name);
|
||||
val.setIndexSupplier(node);
|
||||
return node.var;
|
||||
|
||||
if (val.indexSupplier() instanceof VariableNode prevNode) {
|
||||
prevNode.list().remove(val);
|
||||
}
|
||||
|
||||
var node = new Node(val, null, last);
|
||||
var node = new VariableNode(val, null, last);
|
||||
|
||||
if (last != null) {
|
||||
assert first != null;
|
||||
@ -105,28 +99,17 @@ public final class VariableList {
|
||||
first = last = node;
|
||||
}
|
||||
|
||||
map.put(val.name, node);
|
||||
varMap.put(val, node);
|
||||
val.setIndexSupplier(node);
|
||||
|
||||
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) {
|
||||
if (var == null) return null;
|
||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||
|
||||
if (var == null) return null;
|
||||
|
||||
var node = varMap.get(var);
|
||||
if (node == null) return null;
|
||||
|
||||
@ -151,28 +134,18 @@ public final class VariableList {
|
||||
node.next = null;
|
||||
node.prev = null;
|
||||
|
||||
map.remove(node.var.name);
|
||||
varMap.remove(node.var);
|
||||
|
||||
return node.var;
|
||||
}
|
||||
|
||||
public Variable get(String name) {
|
||||
var res = map.get(name);
|
||||
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 Supplier<VariableIndex> indexer(Variable var) {
|
||||
return varMap.get(var);
|
||||
}
|
||||
|
||||
public int size() {
|
||||
if (frozen()) return frozenList.size();
|
||||
else return map.size();
|
||||
else return varMap.size();
|
||||
}
|
||||
|
||||
public void freeze() {
|
||||
@ -195,7 +168,7 @@ public final class VariableList {
|
||||
public Iterable<Variable> all() {
|
||||
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
||||
else return () -> new Iterator<Variable>() {
|
||||
private Node curr = first;
|
||||
private VariableNode curr = first;
|
||||
|
||||
@Override public boolean hasNext() {
|
||||
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) {
|
||||
indexConverter = map;
|
||||
return this;
|
||||
}
|
||||
|
||||
public VariableList(IntSupplier offset) {
|
||||
public VariableList(VariableIndex.IndexType type, IntSupplier offset) {
|
||||
this.indexType = type;
|
||||
this.offset = offset;
|
||||
}
|
||||
public VariableList(int offset) {
|
||||
public VariableList(VariableIndex.IndexType type, int offset) {
|
||||
this.indexType = type;
|
||||
this.offset = () -> offset;
|
||||
}
|
||||
public VariableList(VariableList prev) {
|
||||
public VariableList(VariableIndex.IndexType type, VariableList prev) {
|
||||
this.indexType = type;
|
||||
this.offset = prev::size;
|
||||
}
|
||||
public VariableList() {
|
||||
public VariableList(VariableIndex.IndexType type) {
|
||||
this.indexType = type;
|
||||
this.offset = null;
|
||||
}
|
||||
}
|
||||
|
@ -34,9 +34,7 @@ public class VariableNode extends Node implements AssignableNode {
|
||||
|
||||
if (!pollute) target.add(Instruction.discard());
|
||||
}
|
||||
else if (pollute) {
|
||||
target.add(_i -> Instruction.loadVar(i.index()));
|
||||
}
|
||||
else if (pollute) target.add(_i -> i.index().toGet());
|
||||
}
|
||||
|
||||
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));
|
||||
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) {
|
||||
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 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) {
|
||||
|
@ -1,8 +1,7 @@
|
||||
package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
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.functions.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ScopeValue;
|
||||
|
||||
public final class Frame {
|
||||
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
|
||||
*/
|
||||
public final Value[][] captures;
|
||||
public final List<Value[]> locals = new ArrayList<>();
|
||||
public final Value[] locals;
|
||||
public final Value[][] capturables;
|
||||
public final Value argsVal;
|
||||
public Value self;
|
||||
public Value fakeArgs;
|
||||
@ -111,9 +110,20 @@ public final class Frame {
|
||||
public final Environment env;
|
||||
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];
|
||||
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];
|
||||
@ -216,12 +226,13 @@ public final class Frame {
|
||||
if (newCtx != tryCtx) {
|
||||
switch (newCtx.state) {
|
||||
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;
|
||||
stackPtr = tryCtx.restoreStackPtr;
|
||||
break;
|
||||
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;
|
||||
stackPtr = tryCtx.restoreStackPtr;
|
||||
default:
|
||||
@ -237,7 +248,7 @@ public final class Frame {
|
||||
}
|
||||
else {
|
||||
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()) {
|
||||
codePtr = tryCtx.finallyStart;
|
||||
@ -357,18 +368,18 @@ public final class Frame {
|
||||
* Gets an object proxy of the capture locals
|
||||
*/
|
||||
public ObjectValue getCaptureScope() {
|
||||
// throw new RuntimeException("Not supported");
|
||||
throw new RuntimeException("Not supported");
|
||||
|
||||
var names = new String[captures.length];
|
||||
var map = DebugContext.get(env).getMapOrEmpty(function);
|
||||
// var names = new String[captures.length];
|
||||
// var map = DebugContext.get(env).getMapOrEmpty(function);
|
||||
|
||||
for (int i = 0; i < captures.length; i++) {
|
||||
var name = "capture_" + (i - 2);
|
||||
if (i < map.captureNames.length) name = map.captureNames[i];
|
||||
names[i] = name;
|
||||
}
|
||||
// for (int i = 0; i < captures.length; i++) {
|
||||
// var name = "capture_" + (i - 2);
|
||||
// if (i < map.captureNames.length) name = map.captureNames[i];
|
||||
// names[i] = name;
|
||||
// }
|
||||
|
||||
return new ScopeValue(captures, names);
|
||||
// return new ScopeValue(captures, names);
|
||||
}
|
||||
/**
|
||||
* Gets an array proxy of the local locals
|
||||
@ -423,8 +434,13 @@ public final class Frame {
|
||||
|
||||
var i = 0;
|
||||
|
||||
for (i = 0; i < func.body.localsN; i++) {
|
||||
this.locals.add(new Value[] { Value.UNDEFINED });
|
||||
this.locals = new Value[func.body.localsN];
|
||||
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) {
|
||||
int i = instr.get(0);
|
||||
|
||||
frame.push(frame.getVar(i)[0]);
|
||||
frame.push(frame.getVar(i));
|
||||
frame.codePtr++;
|
||||
|
||||
return null;
|
||||
@ -179,7 +179,7 @@ public class InstructionRunner {
|
||||
var captures = new Value[instr.params.length - 5][];
|
||||
|
||||
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);
|
||||
@ -278,7 +278,7 @@ public class InstructionRunner {
|
||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||
int i = instr.get(0);
|
||||
|
||||
frame.getVar(i)[0] = val;
|
||||
frame.setVar(i, val);
|
||||
frame.codePtr++;
|
||||
|
||||
return null;
|
||||
@ -340,8 +340,6 @@ public class InstructionRunner {
|
||||
frame.stackPtr -= 1;
|
||||
var ptr = frame.stackPtr;
|
||||
|
||||
// for (var i = op.operands - 1; i >= 0; i--) args[i] = frame.pop();
|
||||
|
||||
switch (op) {
|
||||
case ADD:
|
||||
res = Value.add(env, stack[ptr - 1], stack[ptr]);
|
||||
@ -500,29 +498,34 @@ public class InstructionRunner {
|
||||
frame.codePtr++;
|
||||
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) {
|
||||
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++;
|
||||
return null;
|
||||
}
|
||||
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++;
|
||||
return null;
|
||||
}
|
||||
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++) {
|
||||
frame.locals.remove(frame.locals.size() - 1);
|
||||
}
|
||||
// TODO: Remove if safe to do so
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -561,6 +564,7 @@ public class InstructionRunner {
|
||||
case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame);
|
||||
case LOAD_CALLEE: return execLoadCallee(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 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] = "\n}";
|
||||
|
||||
print(parts);
|
||||
|
||||
const res = compile(stringBuild(parts))();
|
||||
return res;
|
||||
};
|
||||
|
Loading…
Reference in New Issue
Block a user