refactor: use new system to reorder variables that overlaps neighboring scopes
This commit is contained in:
parent
641d4d1863
commit
7c74df4d36
@ -106,6 +106,7 @@ 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++;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
@ -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::parentVarOffset);
|
||||||
|
protected final VariableList captured = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::parentCapOffset);
|
||||||
|
|
||||||
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,10 +25,30 @@ 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) {
|
||||||
|
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;
|
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,34 @@ 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();
|
if (parent != null) return parent.variableOffset() + variables.size();
|
||||||
else return variables.size();
|
else return variables.size();
|
||||||
}
|
}
|
||||||
|
public final int capturedOffset() {
|
||||||
/**
|
if (parent != null) return parent.capturedOffset() + captured.size();
|
||||||
* Adds this variable to the current function's locals record. Capturable indicates whether or not the variable
|
else return localsCount() + captured.size();
|
||||||
* 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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public int localsCount() { return 0; }
|
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 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.allocCount();
|
||||||
|
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 +162,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 +199,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) {
|
||||||
|
@ -18,7 +18,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();
|
||||||
@ -357,18 +356,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
|
||||||
|
@ -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]);
|
||||||
|
@ -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;
|
||||||
};
|
};
|
||||||
@ -310,4 +308,4 @@ setGlobalPrototype("string", String.prototype);
|
|||||||
setGlobalPrototype("number", Number.prototype);
|
setGlobalPrototype("number", Number.prototype);
|
||||||
setGlobalPrototype("boolean", Boolean.prototype);
|
setGlobalPrototype("boolean", Boolean.prototype);
|
||||||
setGlobalPrototype("symbol", Symbol.prototype);
|
setGlobalPrototype("symbol", Symbol.prototype);
|
||||||
setGlobalPrototype("object", Object.prototype);
|
setGlobalPrototype("object", Object.prototype);
|
||||||
|
Loading…
Reference in New Issue
Block a user