a clusterfuck of fixes with let and const
This commit is contained in:
parent
807b3918fa
commit
5b4adf5286
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
|
import java.util.function.Consumer;
|
||||||
import java.util.function.IntFunction;
|
import java.util.function.IntFunction;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.FunctionBody;
|
import me.topchetoeu.jscript.common.FunctionBody;
|
||||||
@ -12,15 +13,28 @@ import me.topchetoeu.jscript.common.environment.Environment;
|
|||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap;
|
||||||
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScope;
|
|
||||||
import me.topchetoeu.jscript.compilation.scope.Scope;
|
import me.topchetoeu.jscript.compilation.scope.Scope;
|
||||||
|
|
||||||
public final class CompileResult {
|
public final class CompileResult {
|
||||||
|
public static final class ChildData {
|
||||||
|
public final int id;
|
||||||
|
public final CompileResult result;
|
||||||
|
|
||||||
|
public ChildData(int id, CompileResult result) {
|
||||||
|
this.result = result;
|
||||||
|
this.id = id;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public final List<IntFunction<Instruction>> instructions;
|
public final List<IntFunction<Instruction>> instructions;
|
||||||
|
// public final List<Supplier<CompileResult>> childrenTasks;
|
||||||
public final List<CompileResult> children;
|
public final List<CompileResult> children;
|
||||||
public final FunctionMapBuilder map;
|
public final FunctionMapBuilder map;
|
||||||
public final Environment env;
|
public final Environment env;
|
||||||
public int length;
|
public int length;
|
||||||
|
public Runnable buildTask = () -> {
|
||||||
|
throw new IllegalStateException("Compile result is not ready to be built");
|
||||||
|
};
|
||||||
public final Scope scope;
|
public final Scope scope;
|
||||||
|
|
||||||
public int temp() {
|
public int temp() {
|
||||||
@ -68,11 +82,16 @@ public final class CompileResult {
|
|||||||
setLocationAndDebug(instructions.size() - 1, loc, type);
|
setLocationAndDebug(instructions.size() - 1, loc, type);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult addChild(CompileResult child) {
|
public int addChild(CompileResult res) {
|
||||||
this.children.add(child);
|
this.children.add(res);
|
||||||
return child;
|
return this.children.size() - 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// public int addChild(Supplier<CompileResult> supplier) {
|
||||||
|
// this.childrenTasks.add(() -> supplier.get());
|
||||||
|
// return childrenTasks.size() - 1;
|
||||||
|
// }
|
||||||
|
|
||||||
public Instruction[] instructions() {
|
public Instruction[] instructions() {
|
||||||
var res = new Instruction[instructions.size()];
|
var res = new Instruction[instructions.size()];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
@ -106,15 +125,17 @@ public final class CompileResult {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult subtarget() {
|
public CompileResult subtarget() {
|
||||||
return new CompileResult(new LocalScope(scope), this);
|
return new CompileResult(new Scope(scope), this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public CompileResult(Environment env, Scope scope) {
|
public CompileResult(Environment env, Scope scope, int length, Consumer<CompileResult> task) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
instructions = new ArrayList<>();
|
this.instructions = new ArrayList<>();
|
||||||
children = new LinkedList<>();
|
this.children = new LinkedList<>();
|
||||||
map = FunctionMap.builder();
|
this.map = FunctionMap.builder();
|
||||||
this.env = env;
|
this.env = env;
|
||||||
|
this.length = length;
|
||||||
|
this.buildTask = () -> task.accept(this);
|
||||||
}
|
}
|
||||||
private CompileResult(Scope scope, CompileResult parent) {
|
private CompileResult(Scope scope, CompileResult parent) {
|
||||||
this.scope = scope;
|
this.scope = scope;
|
||||||
|
@ -43,7 +43,7 @@ public class CompoundNode extends Node {
|
|||||||
|
|
||||||
if (alloc) {
|
if (alloc) {
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
subtarget.add(Instruction.stackFree(subtarget.scope.allocCount()));
|
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!polluted && pollute) {
|
if (!polluted && pollute) {
|
||||||
|
@ -13,12 +13,9 @@ import me.topchetoeu.jscript.compilation.control.ReturnNode;
|
|||||||
public class FunctionArrowNode extends FunctionNode {
|
public class FunctionArrowNode extends FunctionNode {
|
||||||
@Override public String name() { return null; }
|
@Override public String name() { return null; }
|
||||||
|
|
||||||
@Override protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
|
||||||
target.add(Instruction.loadFunc(id, true, false, true, null, captures));
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
compile(target, pollute, false, name, null, bp);
|
var id = target.addChild(compileBody(target, false, name, null));
|
||||||
|
target.add(_i -> Instruction.loadFunc(id, true, false, true, null, captures(id, target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {
|
public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {
|
||||||
|
@ -3,12 +3,13 @@ package me.topchetoeu.jscript.compilation;
|
|||||||
import me.topchetoeu.jscript.common.Instruction;
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Operation;
|
import me.topchetoeu.jscript.common.Operation;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
|
import me.topchetoeu.jscript.common.environment.Environment;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScope;
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
import me.topchetoeu.jscript.runtime.exceptions.SyntaxException;
|
||||||
|
|
||||||
public abstract class FunctionNode extends Node {
|
public abstract class FunctionNode extends Node {
|
||||||
@ -35,87 +36,90 @@ public abstract class FunctionNode extends Node {
|
|||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
// protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) {
|
||||||
target.add(Instruction.loadFunc(id, true, true, false, name, captures));
|
// target.add(Instruction.loadFunc(id, true, true, false, name, captures));
|
||||||
|
// }
|
||||||
|
|
||||||
|
protected final int[] captures(int id, CompileResult target) {
|
||||||
|
return ((FunctionScope)target.children.get(id).scope).getCaptureIndices();
|
||||||
}
|
}
|
||||||
|
|
||||||
private CompileResult compileBody(CompileResult target, boolean hasArgs, String name, String selfName, boolean pollute, BreakpointType bp) {
|
public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, boolean hasArgs, String _name, String selfName) {
|
||||||
var env = target.env.child()
|
var name = this.name() != null ? this.name() : _name;
|
||||||
|
|
||||||
|
env = env.child()
|
||||||
.remove(LabelContext.BREAK_CTX)
|
.remove(LabelContext.BREAK_CTX)
|
||||||
.remove(LabelContext.CONTINUE_CTX);
|
.remove(LabelContext.CONTINUE_CTX);
|
||||||
|
|
||||||
var funcScope = new FunctionScope(target.scope);
|
return new CompileResult(env, scope, params.params.size(), target -> {
|
||||||
var subtarget = new CompileResult(env, new LocalScope(funcScope));
|
if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs());
|
||||||
|
|
||||||
subtarget.length = params.params.size();
|
if (hasArgs) {
|
||||||
|
var argsVar = scope.defineStrict(new Variable("arguments", true), loc());
|
||||||
if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs());
|
target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
|
||||||
|
|
||||||
if (hasArgs) {
|
|
||||||
var argsVar = funcScope.defineParam("arguments", true, loc());
|
|
||||||
subtarget.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (params.params.size() > 0) {
|
|
||||||
if (params.params.size() > 1) subtarget.add(Instruction.dup(params.params.size() - 1));
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
for (var param : params.params) {
|
|
||||||
if (funcScope.hasArg(param.name)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
|
|
||||||
if (!JavaScript.checkVarName(param.name)) {
|
|
||||||
throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name));
|
|
||||||
}
|
|
||||||
var varI = funcScope.defineParam(param.name, false, param.loc);
|
|
||||||
|
|
||||||
subtarget.add(Instruction.loadMember(i++));
|
|
||||||
|
|
||||||
if (param.node != null) {
|
|
||||||
var end = new DeferredIntSupplier();
|
|
||||||
|
|
||||||
subtarget.add(Instruction.dup());
|
|
||||||
subtarget.add(Instruction.pushUndefined());
|
|
||||||
subtarget.add(Instruction.operation(Operation.EQUALS));
|
|
||||||
subtarget.add(Instruction.jmpIfNot(end));
|
|
||||||
subtarget.add(Instruction.discard());
|
|
||||||
param.node.compile(subtarget, pollute);
|
|
||||||
|
|
||||||
end.set(subtarget.size());
|
|
||||||
}
|
|
||||||
|
|
||||||
subtarget.add(Instruction.storeVar(varI.index()));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (params.restName != null) {
|
if (params.params.size() > 0) {
|
||||||
if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
|
if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1));
|
||||||
var restVar = funcScope.defineParam(params.restName, true, params.restLocation);
|
var i = 0;
|
||||||
subtarget.add(Instruction.loadRestArgs(params.params.size()));
|
|
||||||
subtarget.add(_i -> Instruction.storeVar(restVar.index()));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (selfName != null && !funcScope.hasArg(name)) {
|
for (var param : params.params) {
|
||||||
var i = funcScope.defineParam(selfName, true, end);
|
if (scope.has(param.name, false)) throw new SyntaxException(param.loc, "Duplicate parameter name not allowed");
|
||||||
|
if (!JavaScript.checkVarName(param.name)) {
|
||||||
|
throw new SyntaxException(param.loc, String.format("Unexpected identifier '%s'", param.name));
|
||||||
|
}
|
||||||
|
var varI = scope.define(new Variable(param.name, false), param.loc);
|
||||||
|
|
||||||
subtarget.add(Instruction.loadCallee());
|
target.add(Instruction.loadMember(i++));
|
||||||
subtarget.add(_i -> Instruction.storeVar(i.index(), false));
|
|
||||||
}
|
|
||||||
|
|
||||||
body.resolve(subtarget);
|
if (param.node != null) {
|
||||||
body.compile(subtarget, false, false, BreakpointType.NONE);
|
var end = new DeferredIntSupplier();
|
||||||
|
|
||||||
subtarget.scope.end();
|
target.add(Instruction.dup());
|
||||||
funcScope.end();
|
target.add(Instruction.pushUndefined());
|
||||||
|
target.add(Instruction.operation(Operation.EQUALS));
|
||||||
|
target.add(Instruction.jmpIfNot(end));
|
||||||
|
target.add(Instruction.discard());
|
||||||
|
param.node.compile(target, true);
|
||||||
|
|
||||||
if (pollute) compileLoadFunc(target, target.children.size(), funcScope.getCaptureIndices(), name);
|
end.set(target.size());
|
||||||
|
}
|
||||||
|
|
||||||
return target.addChild(subtarget);
|
target.add(_i -> Instruction.storeVar(varI.index()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (params.restName != null) {
|
||||||
|
if (scope.has(params.restName, false)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed");
|
||||||
|
var restVar = scope.defineParam(new Variable(params.restName, false), params.restLocation);
|
||||||
|
target.add(Instruction.loadRestArgs(params.params.size()));
|
||||||
|
target.add(_i -> Instruction.storeVar(restVar.index()));
|
||||||
|
}
|
||||||
|
|
||||||
|
if (selfName != null && !scope.has(name, false)) {
|
||||||
|
var i = scope.defineParam(new Variable(selfName, true), end);
|
||||||
|
|
||||||
|
target.add(Instruction.loadCallee());
|
||||||
|
target.add(_i -> Instruction.storeVar(i.index(), false));
|
||||||
|
}
|
||||||
|
|
||||||
|
body.resolve(target);
|
||||||
|
body.compile(target, lastReturn, false, BreakpointType.NONE);
|
||||||
|
|
||||||
|
scope.end();
|
||||||
|
|
||||||
|
for (var child : target.children) child.buildTask.run();
|
||||||
|
|
||||||
|
scope.finish();
|
||||||
|
});
|
||||||
|
|
||||||
|
// if (pollute) compileLoadFunc(target, target.children.size(), subscope.getCaptureIndices(), name);
|
||||||
|
// return target.addChild(subtarget);
|
||||||
|
}
|
||||||
|
public final CompileResult compileBody(CompileResult parent, boolean hasArgs, String name, String selfName) {
|
||||||
|
return compileBody(parent.env, new FunctionScope(parent.scope), false, hasArgs, name, selfName);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void compile(CompileResult target, boolean pollute, boolean hasArgs, String name, String selfName, BreakpointType bp) {
|
|
||||||
if (this.name() != null) name = this.name();
|
|
||||||
|
|
||||||
compileBody(target, hasArgs, name, selfName, pollute, bp);
|
|
||||||
}
|
|
||||||
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);
|
||||||
public void compile(CompileResult target, boolean pollute, String name) {
|
public void compile(CompileResult target, boolean pollute, String name) {
|
||||||
compile(target, pollute, name, BreakpointType.NONE);
|
compile(target, pollute, name, BreakpointType.NONE);
|
||||||
|
@ -1,7 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class FunctionStatementNode extends FunctionNode {
|
public class FunctionStatementNode extends FunctionNode {
|
||||||
@ -10,11 +12,12 @@ public class FunctionStatementNode extends FunctionNode {
|
|||||||
@Override public String name() { return name; }
|
@Override public String name() { return name; }
|
||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
target.scope.define(name, false, end);
|
target.scope.define(new Variable(name, false), end);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
compile(target, true, true, name, this.name, bp);
|
var id = target.addChild(compileBody(target, false, name, null));
|
||||||
|
target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target)));
|
||||||
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
|
target.add(VariableNode.toSet(target, end, this.name, pollute, true));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
|
import me.topchetoeu.jscript.common.Instruction;
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
import me.topchetoeu.jscript.common.parsing.Location;
|
||||||
|
|
||||||
@ -8,8 +9,13 @@ public class FunctionValueNode extends FunctionNode {
|
|||||||
|
|
||||||
@Override public String name() { return name; }
|
@Override public String name() { return name; }
|
||||||
|
|
||||||
|
// @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
|
// compileBody(target, pollute, true, name, null, bp);
|
||||||
|
// }
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
|
||||||
compile(target, pollute, true, name, null, bp);
|
var id = target.addChild(compileBody(target, false, name, null));
|
||||||
|
target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target)));
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {
|
||||||
|
@ -1,10 +1,9 @@
|
|||||||
package me.topchetoeu.jscript.compilation;
|
package me.topchetoeu.jscript.compilation;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.Instruction;
|
|
||||||
import me.topchetoeu.jscript.common.Instruction.BreakpointType;
|
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
import me.topchetoeu.jscript.common.environment.Environment;
|
||||||
import me.topchetoeu.jscript.common.parsing.Filename;
|
import me.topchetoeu.jscript.common.parsing.Filename;
|
||||||
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
import me.topchetoeu.jscript.common.parsing.ParseRes;
|
||||||
@ -24,8 +23,7 @@ import me.topchetoeu.jscript.compilation.control.SwitchNode;
|
|||||||
import me.topchetoeu.jscript.compilation.control.ThrowNode;
|
import me.topchetoeu.jscript.compilation.control.ThrowNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.TryNode;
|
import me.topchetoeu.jscript.compilation.control.TryNode;
|
||||||
import me.topchetoeu.jscript.compilation.control.WhileNode;
|
import me.topchetoeu.jscript.compilation.control.WhileNode;
|
||||||
import me.topchetoeu.jscript.compilation.scope.GlobalScope;
|
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
|
||||||
import me.topchetoeu.jscript.compilation.scope.LocalScope;
|
|
||||||
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
import me.topchetoeu.jscript.compilation.values.ArrayNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
import me.topchetoeu.jscript.compilation.values.ObjectNode;
|
||||||
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
import me.topchetoeu.jscript.compilation.values.RegexNode;
|
||||||
@ -326,24 +324,32 @@ public final class JavaScript {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static CompileResult compile(Environment env, Node ...statements) {
|
public static CompileResult compile(Environment env, Node ...statements) {
|
||||||
var target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null);
|
||||||
var stm = new CompoundNode(null, statements);
|
var res = func.compileBody(env, new FunctionScope(true), true, true, null, null);
|
||||||
var argsI = target.scope.defineStrict("arguments", true, null);
|
res.buildTask.run();
|
||||||
target.add(Instruction.loadArgs());
|
return res;
|
||||||
target.add(_i -> Instruction.storeVar(argsI.index()));
|
|
||||||
|
|
||||||
// try {
|
// var target = new CompileResult(env, new FunctionScope(true));
|
||||||
stm.resolve(target);
|
// var stm = ;
|
||||||
stm.compile(target, true, false, BreakpointType.NONE);
|
// var argsI = target.scope.defineStrict(new Variable("arguments", true), null);
|
||||||
// FunctionNode.checkBreakAndCont(target, 0);
|
// target.add(Instruction.loadArgs());
|
||||||
// }
|
// target.add(_i -> Instruction.storeVar(argsI.index()));
|
||||||
// catch (SyntaxException e) {
|
|
||||||
// target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
|
||||||
|
|
||||||
// target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
// // try {
|
||||||
// }
|
// stm.resolve(target);
|
||||||
|
// stm.compile(target, true, false, BreakpointType.NONE);
|
||||||
|
// // FunctionNode.checkBreakAndCont(target, 0);
|
||||||
|
// // }
|
||||||
|
// // catch (SyntaxException e) {
|
||||||
|
// // target = new CompileResult(env, new LocalScope(new GlobalScope()));
|
||||||
|
|
||||||
return target;
|
// // target.add(Instruction.throwSyntax(e)).setLocation(stm.loc());
|
||||||
|
// // }
|
||||||
|
|
||||||
|
// target.scope.end();
|
||||||
|
// target.scope.finish();
|
||||||
|
|
||||||
|
// return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static CompileResult compile(Environment env, Filename filename, String raw) {
|
public static CompileResult compile(Environment env, Filename filename, String raw) {
|
||||||
|
@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
|
|||||||
import me.topchetoeu.jscript.common.parsing.Parsing;
|
import me.topchetoeu.jscript.common.parsing.Parsing;
|
||||||
import me.topchetoeu.jscript.common.parsing.Source;
|
import me.topchetoeu.jscript.common.parsing.Source;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class VariableDeclareNode extends Node {
|
public class VariableDeclareNode extends Node {
|
||||||
@ -31,7 +32,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
if (!declType.strict) {
|
if (!declType.strict) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
target.scope.define(entry.name, false, entry.location);
|
target.scope.define(new Variable(entry.name, false), entry.location);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -39,7 +40,7 @@ public class VariableDeclareNode extends Node {
|
|||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
for (var entry : values) {
|
for (var entry : values) {
|
||||||
if (entry.name == null) continue;
|
if (entry.name == null) continue;
|
||||||
if (declType.strict) target.scope.defineStrict(entry.name, declType.readonly, entry.location);
|
if (declType.strict) target.scope.defineStrict(new Variable(entry.name, declType.readonly), entry.location);
|
||||||
|
|
||||||
if (entry.value != null) {
|
if (entry.value != null) {
|
||||||
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER);
|
||||||
|
@ -13,6 +13,7 @@ import me.topchetoeu.jscript.compilation.JavaScript;
|
|||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForInNode extends Node {
|
public class ForInNode extends Node {
|
||||||
@ -24,11 +25,11 @@ public class ForInNode extends Node {
|
|||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
if (declType != null && !declType.strict) target.scope.define(varName, false, loc());
|
if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), loc());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation);
|
||||||
|
|
||||||
object.compile(target, true, BreakpointType.STEP_OVER);
|
object.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.keys(true));
|
target.add(Instruction.keys(true));
|
||||||
|
@ -47,7 +47,7 @@ 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(Instruction.stackFree(subtarget.scope.allocCount()));
|
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) {
|
||||||
|
@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.JavaScript;
|
|||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
import me.topchetoeu.jscript.compilation.values.VariableNode;
|
||||||
|
|
||||||
public class ForOfNode extends Node {
|
public class ForOfNode extends Node {
|
||||||
@ -23,11 +24,11 @@ public class ForOfNode extends Node {
|
|||||||
|
|
||||||
@Override public void resolve(CompileResult target) {
|
@Override public void resolve(CompileResult target) {
|
||||||
body.resolve(target);
|
body.resolve(target);
|
||||||
if (declType != null && !declType.strict) target.scope.define(varName, false, varLocation);
|
if (declType != null && !declType.strict) target.scope.define(new Variable(varName, false), varLocation);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public void compile(CompileResult target, boolean pollute) {
|
@Override public void compile(CompileResult target, boolean pollute) {
|
||||||
if (declType != null && declType.strict) target.scope.defineStrict(varName, declType.readonly, varLocation);
|
if (declType != null && declType.strict) target.scope.defineStrict(new Variable(varName, declType.readonly), varLocation);
|
||||||
|
|
||||||
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
iterable.compile(target, true, BreakpointType.STEP_OVER);
|
||||||
target.add(Instruction.dup());
|
target.add(Instruction.dup());
|
||||||
|
@ -65,7 +65,7 @@ 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(Instruction.stackFree(subtarget.scope.allocCount()));
|
subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount()));
|
||||||
|
|
||||||
int endI = subtarget.size();
|
int endI = subtarget.size();
|
||||||
end.set(endI);
|
end.set(endI);
|
||||||
|
@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier;
|
|||||||
import me.topchetoeu.jscript.compilation.JavaScript;
|
import me.topchetoeu.jscript.compilation.JavaScript;
|
||||||
import me.topchetoeu.jscript.compilation.LabelContext;
|
import me.topchetoeu.jscript.compilation.LabelContext;
|
||||||
import me.topchetoeu.jscript.compilation.Node;
|
import me.topchetoeu.jscript.compilation.Node;
|
||||||
|
import me.topchetoeu.jscript.compilation.scope.Variable;
|
||||||
|
|
||||||
public class TryNode extends Node {
|
public class TryNode extends Node {
|
||||||
public final CompoundNode tryBody;
|
public final CompoundNode tryBody;
|
||||||
@ -42,7 +43,7 @@ public class TryNode extends Node {
|
|||||||
|
|
||||||
if (captureName != null) {
|
if (captureName != null) {
|
||||||
var subtarget = target.subtarget();
|
var subtarget = target.subtarget();
|
||||||
subtarget.scope.defineStrict(captureName, false, catchBody.loc());
|
subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc());
|
||||||
catchBody.compile(subtarget, false);
|
catchBody.compile(subtarget, false);
|
||||||
subtarget.scope.end();
|
subtarget.scope.end();
|
||||||
}
|
}
|
||||||
|
@ -1,79 +1,113 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
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().setIndexMap(v -> ~v);
|
||||||
private final VariableList specials = new VariableList();
|
private final VariableList specials = new VariableList();
|
||||||
private final VariableList locals = new VariableList(specials);
|
private final VariableList locals = new VariableList(specials);
|
||||||
private HashMap<VariableDescriptor, VariableDescriptor> childToParent = new HashMap<>();
|
private final HashMap<Variable, Variable> childToParent = new HashMap<>();
|
||||||
|
private final HashSet<String> blacklistNames = new HashSet<>();
|
||||||
|
|
||||||
|
private final Scope captureParent;
|
||||||
|
|
||||||
|
public final boolean passtrough;
|
||||||
|
|
||||||
private void removeCapture(String name) {
|
private void removeCapture(String name) {
|
||||||
var res = captures.remove(name);
|
var res = captures.remove(name);
|
||||||
if (res != null) childToParent.remove(res);
|
if (res != null) {
|
||||||
|
childToParent.remove(res);
|
||||||
|
res.setIndexSupplier(() -> { throw new SyntaxException(null, res.name + " has been shadowed"); });
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public VariableDescriptor define(String name, boolean readonly, Location loc) {
|
@Override public Variable define(Variable var, Location loc) {
|
||||||
var old = locals.get(name);
|
checkNotEnded();
|
||||||
if (old != null) return old;
|
if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
// if (specials.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
// if (blacklistNames.contains(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
removeCapture(name);
|
if (passtrough) {
|
||||||
return locals.add(name, readonly);
|
blacklistNames.add(var.name);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
removeCapture(var.name);
|
||||||
|
return locals.add(var);
|
||||||
}
|
}
|
||||||
@Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) {
|
@Override public Variable defineStrict(Variable var, Location loc) {
|
||||||
if (locals.has(name)) throw alreadyDefinedErr(loc, name);
|
checkNotEnded();
|
||||||
else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes");
|
if (locals.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
else return parent.defineStrict(name, readonly, loc);
|
if (specials.has(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;
|
||||||
}
|
}
|
||||||
public VariableDescriptor defineParam(String name, boolean readonly, Location loc) {
|
public Variable defineParam(Variable var, Location loc) {
|
||||||
return specials.add(name, readonly);
|
return specials.add(var);
|
||||||
}
|
|
||||||
public boolean hasArg(String name) {
|
|
||||||
return specials.has(name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public VariableDescriptor get(String name, boolean capture) {
|
@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;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override public Variable get(String name, boolean capture) {
|
||||||
|
var superRes = super.get(name, capture);
|
||||||
|
if (superRes != null) return superRes;
|
||||||
|
|
||||||
if (specials.has(name)) return specials.get(name);
|
if (specials.has(name)) return specials.get(name);
|
||||||
if (locals.has(name)) return locals.get(name);
|
if (locals.has(name)) return locals.get(name);
|
||||||
if (captures.has(name)) return captures.get(name);
|
if (captures.has(name)) return captures.get(name);
|
||||||
|
if (captureParent == null) return null;
|
||||||
|
|
||||||
var parentVar = parent.get(name, true);
|
var parentVar = captureParent.get(name, true);
|
||||||
if (parentVar == null) return null;
|
if (parentVar == null) return null;
|
||||||
|
|
||||||
var childVar = captures.add(parentVar);
|
var childVar = captures.add(parentVar.clone());
|
||||||
|
|
||||||
childToParent.put(childVar, parentVar);
|
childToParent.put(childVar, parentVar);
|
||||||
|
|
||||||
return childVar;
|
return childVar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean has(String name) {
|
@Override public boolean has(String name, boolean capture) {
|
||||||
if (specials.has(name)) return true;
|
if (specials.has(name)) return true;
|
||||||
if (locals.has(name)) return true;
|
if (locals.has(name)) return true;
|
||||||
if (captures.has(name)) return true;
|
|
||||||
if (parent != null) return parent.has(name);
|
if (capture) {
|
||||||
|
if (captures.has(name)) return true;
|
||||||
|
if (captureParent != null) return captureParent.has(name, true);
|
||||||
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public boolean end() {
|
@Override public boolean finish() {
|
||||||
if (!super.end()) return false;
|
if (!super.finish()) return false;
|
||||||
|
|
||||||
captures.freeze();
|
captures.freeze();
|
||||||
locals.freeze();
|
locals.freeze();
|
||||||
|
specials.freeze();
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public int localsCount() {
|
@Override public int allocCount() {
|
||||||
return locals.size() + specials.size();
|
return 0;
|
||||||
}
|
}
|
||||||
@Override public int capturesCount() {
|
@Override public int capturesCount() {
|
||||||
return captures.size();
|
return captures.size();
|
||||||
}
|
}
|
||||||
@Override public int allocCount() {
|
@Override public int localsCount() {
|
||||||
return 0;
|
return locals.size() + specials.size() + super.allocCount();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int offset() {
|
public int offset() {
|
||||||
@ -84,7 +118,7 @@ public class FunctionScope extends Scope {
|
|||||||
var res = new int[captures.size()];
|
var res = new int[captures.size()];
|
||||||
var i = 0;
|
var i = 0;
|
||||||
|
|
||||||
for (var el : captures) {
|
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();
|
||||||
i++;
|
i++;
|
||||||
@ -93,10 +127,15 @@ public class FunctionScope extends Scope {
|
|||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
public FunctionScope() {
|
|
||||||
super();
|
|
||||||
}
|
|
||||||
public FunctionScope(Scope parent) {
|
public FunctionScope(Scope parent) {
|
||||||
super(parent);
|
super();
|
||||||
|
if (parent.finished()) throw new RuntimeException("Parent is finished");
|
||||||
|
this.captureParent = parent;
|
||||||
|
this.passtrough = false;
|
||||||
|
}
|
||||||
|
public FunctionScope(boolean passtrough) {
|
||||||
|
super();
|
||||||
|
this.captureParent = null;
|
||||||
|
this.passtrough = passtrough;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,33 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
|
|
||||||
public final class GlobalScope extends Scope {
|
|
||||||
@Override public VariableDescriptor define(String name, boolean readonly, Location loc) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override public VariableDescriptor get(String name, boolean capture) {
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
@Override public int offset() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@Override public boolean has(String name) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int localsCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@Override public int capturesCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
@Override public int allocCount() {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
public GlobalScope() { super(); }
|
|
||||||
}
|
|
@ -1,69 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
|
||||||
|
|
||||||
import me.topchetoeu.jscript.common.parsing.Location;
|
|
||||||
|
|
||||||
public final class LocalScope extends Scope {
|
|
||||||
private final VariableList locals = new VariableList(() -> {
|
|
||||||
if (parent != null) return parent.offset();
|
|
||||||
else return 0;
|
|
||||||
});
|
|
||||||
|
|
||||||
@Override public int offset() {
|
|
||||||
if (parent != null) return parent.offset() + locals.size();
|
|
||||||
else return locals.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public VariableDescriptor define(String name, boolean readonly, Location loc) {
|
|
||||||
if (locals.has(name)) throw alreadyDefinedErr(loc, name);
|
|
||||||
|
|
||||||
return parent.define(name, readonly, loc);
|
|
||||||
}
|
|
||||||
@Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) {
|
|
||||||
if (locals.has(name)) throw alreadyDefinedErr(loc, name);
|
|
||||||
return locals.add(name, readonly);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public VariableDescriptor get(String name, boolean capture) {
|
|
||||||
var res = locals.get(name);
|
|
||||||
|
|
||||||
if (res != null) return res;
|
|
||||||
if (parent != null) return parent.get(name, capture);
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean has(String name) {
|
|
||||||
if (locals.has(name)) return true;
|
|
||||||
if (parent != null) return parent.has(name);
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public boolean end() {
|
|
||||||
if (!super.end()) return false;
|
|
||||||
|
|
||||||
this.locals.freeze();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override public int localsCount() {
|
|
||||||
if (parent == null) return 0;
|
|
||||||
else return parent.localsCount();
|
|
||||||
}
|
|
||||||
@Override public int capturesCount() {
|
|
||||||
if (parent == null) return 0;
|
|
||||||
else return parent.capturesCount();
|
|
||||||
}
|
|
||||||
@Override public int allocCount() {
|
|
||||||
return locals.size();
|
|
||||||
}
|
|
||||||
|
|
||||||
public Iterable<VariableDescriptor> all() {
|
|
||||||
return () -> locals.iterator();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public LocalScope(Scope parent) {
|
|
||||||
super(parent);
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,24 +1,60 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
|
import java.util.HashSet;
|
||||||
|
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 abstract class Scope {
|
public class Scope {
|
||||||
public final Scope parent;
|
protected final VariableList variables = new VariableList(this::parentOffset);
|
||||||
private boolean active = true;
|
|
||||||
|
private boolean ended = false;
|
||||||
|
private boolean finished = false;
|
||||||
private Scope child;
|
private Scope child;
|
||||||
|
private List<Scope> prevChildren = new LinkedList<>();
|
||||||
|
|
||||||
|
public final Scope parent;
|
||||||
|
public final HashSet<Variable> captured = new HashSet<>();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Wether or not the scope is going to be entered multiple times.
|
||||||
|
* If set to true, captured variables will be kept as allocations, otherwise will be converted to locals
|
||||||
|
*/
|
||||||
|
public boolean singleEntry = true;
|
||||||
|
|
||||||
|
private final int parentOffset() {
|
||||||
|
if (parent != null) return parent.offset();
|
||||||
|
else return 0;
|
||||||
|
}
|
||||||
|
|
||||||
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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Throws if the scope is ended
|
||||||
|
*/
|
||||||
|
protected final void checkNotEnded() {
|
||||||
|
if (ended) throw new IllegalStateException("Cannot define in an ended scope");
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an ES5-style variable
|
* Defines an ES5-style variable
|
||||||
|
*
|
||||||
* @returns The index supplier of the variable if it is a local, or null if it is a global
|
* @returns The index supplier of the variable if it is a local, or null if it is a global
|
||||||
* @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope
|
* @throws SyntaxException If an ES2015-style variable with the same name exists anywhere from the current function to the current scope
|
||||||
* @throws RuntimeException If the scope is finalized or has an active child
|
* @throws RuntimeException If the scope is finalized or has an active child
|
||||||
*/
|
*/
|
||||||
public abstract VariableDescriptor define(String name, boolean readonly, Location loc);
|
public Variable define(Variable var, Location loc) {
|
||||||
|
checkNotEnded();
|
||||||
|
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
if (parent != null) return parent.define(var, loc);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Defines an ES2015-style variable
|
* Defines an ES2015-style variable
|
||||||
* @param readonly True if const, false if let
|
* @param readonly True if const, false if let
|
||||||
@ -26,29 +62,76 @@ public abstract class Scope {
|
|||||||
* @throws SyntaxException If any variable with the same name exists in the current scope
|
* @throws SyntaxException If any variable with the same name exists in the current scope
|
||||||
* @throws RuntimeException If the scope is finalized or has an active child
|
* @throws RuntimeException If the scope is finalized or has an active child
|
||||||
*/
|
*/
|
||||||
public abstract VariableDescriptor defineStrict(String name, boolean readonly, Location loc);
|
public Variable defineStrict(Variable var, Location loc) {
|
||||||
|
checkNotEnded();
|
||||||
|
if (variables.has(var.name)) throw alreadyDefinedErr(loc, var.name);
|
||||||
|
|
||||||
|
variables.add(var);
|
||||||
|
return var.setIndexSupplier(() -> variables.indexOfKey(var.name));
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* 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
|
||||||
*
|
*
|
||||||
* @param capture Used to signal to the scope that the variable is going to be captured.
|
* @param capture If true, the variable is being captured by a function
|
||||||
* Not passing this could lead to a local variable being optimized out as an ES5-style variable,
|
|
||||||
* which could break the semantics of a capture
|
|
||||||
*/
|
*/
|
||||||
public abstract VariableDescriptor get(String name, boolean capture);
|
public Variable get(String name, boolean capture) {
|
||||||
public abstract boolean has(String name);
|
var res = variables.get(name);
|
||||||
|
if (res != null) return res;
|
||||||
|
if (parent != null) return parent.get(name, capture);
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* Checks if the given variable name is accessible
|
||||||
|
*
|
||||||
|
* @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 (parent != null) return parent.has(name, capture);
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* Gets the index offset from this scope to its children
|
* Gets the index offset from this scope to its children
|
||||||
*/
|
*/
|
||||||
public abstract int offset();
|
public int offset() {
|
||||||
|
if (parent != null) return parent.offset() + variables.size();
|
||||||
|
else return variables.size();
|
||||||
|
}
|
||||||
|
|
||||||
public abstract int localsCount();
|
/**
|
||||||
public abstract int capturesCount();
|
* Adds this variable to the current function's locals record. Capturable indicates whether or not the variable
|
||||||
public abstract int allocCount();
|
* 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 (parent == null) return false;
|
||||||
|
return parent.flattenVariable(variable, capturable);
|
||||||
|
}
|
||||||
|
|
||||||
public boolean end() {
|
public int localsCount() { return 0; }
|
||||||
if (!active) return false;
|
public int capturesCount() { return 0; }
|
||||||
|
public int allocCount() { return variables.size(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ends this scope. This will make it possible for another child to take its place
|
||||||
|
*/
|
||||||
|
public boolean end() {
|
||||||
|
if (ended) return false;
|
||||||
|
|
||||||
|
this.ended = true;
|
||||||
|
|
||||||
|
for (var v : variables.all()) {
|
||||||
|
if (captured.contains(v)) {
|
||||||
|
if (singleEntry) this.flattenVariable(v, true);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
this.flattenVariable(v, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
this.active = false;
|
|
||||||
if (this.parent != null) {
|
if (this.parent != null) {
|
||||||
assert this.parent.child == this;
|
assert this.parent.child == this;
|
||||||
this.parent.child = null;
|
this.parent.child = null;
|
||||||
@ -57,17 +140,39 @@ public abstract class Scope {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public final boolean active() { return active; }
|
/**
|
||||||
|
* Finalizes this scope. The scope will become immutable after this call
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public boolean finish() {
|
||||||
|
if (finished) return false;
|
||||||
|
if (parent != null && !parent.finished) throw new IllegalStateException("Tried to finish a child before the parent was finished");
|
||||||
|
|
||||||
|
this.variables.freeze();
|
||||||
|
this.finished = true;
|
||||||
|
|
||||||
|
for (var child : prevChildren) child.finish();
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final boolean ended() { return ended; }
|
||||||
|
public final boolean finished() { return finished; }
|
||||||
public final Scope child() { return child; }
|
public final Scope child() { return child; }
|
||||||
|
|
||||||
public Scope() {
|
public Scope() {
|
||||||
this.parent = null;
|
this(null);
|
||||||
}
|
}
|
||||||
public Scope(Scope parent) {
|
public Scope(Scope parent) {
|
||||||
if (!parent.active) throw new RuntimeException("Parent is not active");
|
if (parent != null) {
|
||||||
if (parent.child != null) throw new RuntimeException("Parent has an active child");
|
if (parent.ended) throw new RuntimeException("Parent is not active");
|
||||||
|
if (parent.finished) throw new RuntimeException("Parent is finished");
|
||||||
|
if (parent.child != null) throw new RuntimeException("Parent has an active child");
|
||||||
|
|
||||||
this.parent = parent;
|
this.parent = parent;
|
||||||
this.parent.child = this;
|
this.parent.child = this;
|
||||||
|
this.parent.prevChildren.add(this);
|
||||||
|
}
|
||||||
|
else this.parent = null;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,41 @@
|
|||||||
|
package me.topchetoeu.jscript.compilation.scope;
|
||||||
|
|
||||||
|
import java.util.function.IntSupplier;
|
||||||
|
|
||||||
|
public final class Variable {
|
||||||
|
private IntSupplier indexSupplier;
|
||||||
|
private boolean frozen;
|
||||||
|
|
||||||
|
public final boolean readonly;
|
||||||
|
public final String name;
|
||||||
|
|
||||||
|
public final int index() {
|
||||||
|
if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized");
|
||||||
|
return indexSupplier.getAsInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public final void freeze() {
|
||||||
|
this.frozen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Variable setIndexSupplier(IntSupplier index) {
|
||||||
|
this.indexSupplier = index;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
public final IntSupplier indexSupplier() {
|
||||||
|
return indexSupplier;
|
||||||
|
}
|
||||||
|
|
||||||
|
public final Variable clone() {
|
||||||
|
return new Variable(name, readonly).setIndexSupplier(indexSupplier);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Variable(String name, boolean readonly) {
|
||||||
|
this.name = name;
|
||||||
|
this.readonly = readonly;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static Variable of(String name, boolean readonly, int i) {
|
||||||
|
return new Variable(name, readonly).setIndexSupplier(() -> i);
|
||||||
|
}
|
||||||
|
}
|
@ -1,19 +0,0 @@
|
|||||||
package me.topchetoeu.jscript.compilation.scope;
|
|
||||||
|
|
||||||
public abstract class VariableDescriptor {
|
|
||||||
public final boolean readonly;
|
|
||||||
public final String name;
|
|
||||||
|
|
||||||
public abstract int index();
|
|
||||||
|
|
||||||
public VariableDescriptor(String name, boolean readonly) {
|
|
||||||
this.name = name;
|
|
||||||
this.readonly = readonly;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static VariableDescriptor of(String name, boolean readonly, int i) {
|
|
||||||
return new VariableDescriptor(name, readonly) {
|
|
||||||
@Override public int index() { return i; }
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
@ -5,15 +5,17 @@ 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.IntUnaryOperator;
|
||||||
|
import java.util.stream.StreamSupport;
|
||||||
|
|
||||||
public final class VariableList implements Iterable<VariableDescriptor> {
|
public final class VariableList {
|
||||||
private class ListVar extends VariableDescriptor {
|
private final class Node implements IntSupplier {
|
||||||
private ListVar next;
|
public Variable var;
|
||||||
private ListVar prev;
|
public Node next;
|
||||||
private boolean frozen;
|
public Node prev;
|
||||||
private int index;
|
public boolean frozen;
|
||||||
|
public int index;
|
||||||
|
|
||||||
@Override public int index() {
|
@Override public int getAsInt() {
|
||||||
if (frozen) {
|
if (frozen) {
|
||||||
if (offset == null) {
|
if (offset == null) {
|
||||||
return indexConverter == null ? index : indexConverter.applyAsInt(index);
|
return indexConverter == null ? index : indexConverter.applyAsInt(index);
|
||||||
@ -35,41 +37,38 @@ public final class VariableList implements Iterable<VariableDescriptor> {
|
|||||||
return indexConverter == null ? res : indexConverter.applyAsInt(res);
|
return indexConverter == null ? res : indexConverter.applyAsInt(res);
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListVar freeze() {
|
public void freeze() {
|
||||||
if (frozen) return this;
|
if (frozen) return;
|
||||||
this.frozen = true;
|
this.frozen = true;
|
||||||
this.next = null;
|
this.next = null;
|
||||||
if (prev == null) return this;
|
this.var.freeze();
|
||||||
|
if (prev == null) return;
|
||||||
|
|
||||||
this.index = prev.index + 1;
|
this.index = prev.index + 1;
|
||||||
this.next = null;
|
this.next = null;
|
||||||
|
|
||||||
return this;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ListVar(String name, boolean readonly, ListVar next, ListVar prev) {
|
public Node(Variable var, Node next, Node prev) {
|
||||||
super(name, readonly);
|
this.var = var;
|
||||||
|
|
||||||
this.next = next;
|
this.next = next;
|
||||||
this.prev = prev;
|
this.prev = prev;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private ListVar first, last;
|
private Node first, last;
|
||||||
|
|
||||||
private HashMap<String, ListVar> map = new HashMap<>();
|
private final HashMap<String, Node> map = new HashMap<>();
|
||||||
|
private ArrayList<Node> frozenList = null;
|
||||||
private HashMap<String, VariableDescriptor> frozenMap = null;
|
|
||||||
private ArrayList<VariableDescriptor> frozenList = null;
|
|
||||||
|
|
||||||
private final IntSupplier offset;
|
private final IntSupplier offset;
|
||||||
private IntUnaryOperator indexConverter = null;
|
private IntUnaryOperator indexConverter = null;
|
||||||
|
|
||||||
public boolean frozen() {
|
public boolean frozen() {
|
||||||
if (frozenMap != null) {
|
if (frozenList != null) {
|
||||||
assert frozenList != null;
|
assert frozenList != null;
|
||||||
assert frozenMap != null;
|
assert map != null;
|
||||||
assert map == null;
|
|
||||||
assert first == null;
|
assert first == null;
|
||||||
assert last == null;
|
assert last == null;
|
||||||
|
|
||||||
@ -77,95 +76,87 @@ public final class VariableList implements Iterable<VariableDescriptor> {
|
|||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert frozenList == null;
|
assert frozenList == null;
|
||||||
assert frozenMap == null;
|
|
||||||
assert map != null;
|
assert map != null;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDescriptor add(VariableDescriptor val) {
|
private Variable add(Variable val, boolean overlay) {
|
||||||
return add(val.name, val.readonly);
|
|
||||||
}
|
|
||||||
public VariableDescriptor add(String name, boolean readonly) {
|
|
||||||
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
|
||||||
if (map.containsKey(name)) return map.get(name);
|
|
||||||
|
|
||||||
var res = new ListVar(name, readonly, null, last);
|
|
||||||
|
|
||||||
if (last != null) {
|
|
||||||
assert first != null;
|
|
||||||
|
|
||||||
last.next = res;
|
|
||||||
res.prev = last;
|
|
||||||
|
|
||||||
last = res;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
first = last = res;
|
|
||||||
}
|
|
||||||
|
|
||||||
map.put(name, res);
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
|
||||||
public VariableDescriptor remove(String name) {
|
|
||||||
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);
|
||||||
|
val.setIndexSupplier(node);
|
||||||
|
return node.var;
|
||||||
|
}
|
||||||
|
|
||||||
var el = map.get(name);
|
var node = new Node(val, null, last);
|
||||||
if (el == null) return null;
|
|
||||||
|
|
||||||
if (el.prev != null) {
|
if (last != null) {
|
||||||
assert el != first;
|
assert first != null;
|
||||||
el.prev.next = el.next;
|
|
||||||
|
last.next = node;
|
||||||
|
node.prev = last;
|
||||||
|
|
||||||
|
last = node;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert el == first;
|
first = last = node;
|
||||||
|
}
|
||||||
|
|
||||||
|
map.put(val.name, 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) {
|
||||||
|
if (frozen()) throw new RuntimeException("The scope has been frozen");
|
||||||
|
|
||||||
|
var node = map.get(key);
|
||||||
|
if (node == null) return null;
|
||||||
|
|
||||||
|
if (node.prev != null) {
|
||||||
|
assert node != first;
|
||||||
|
node.prev.next = node.next;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
assert node == first;
|
||||||
first = first.next;
|
first = first.next;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (el.next != null) {
|
if (node.next != null) {
|
||||||
assert el != last;
|
assert node != last;
|
||||||
el.next.prev = el.prev;
|
node.next.prev = node.prev;
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
assert el == last;
|
assert node == last;
|
||||||
last = last.prev;
|
last = last.prev;
|
||||||
}
|
}
|
||||||
|
|
||||||
el.next = null;
|
node.next = null;
|
||||||
el.prev = null;
|
node.prev = null;
|
||||||
|
|
||||||
return el;
|
return node.var;
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableDescriptor get(String name) {
|
public Variable get(String name) {
|
||||||
if (frozen()) return frozenMap.get(name);
|
var res = map.get(name);
|
||||||
else return map.get(name);
|
if (res != null) return res.var;
|
||||||
|
else return null;
|
||||||
}
|
}
|
||||||
public VariableDescriptor get(int i) {
|
public int indexOfKey(String name) {
|
||||||
if (frozen()) {
|
return map.get(name).getAsInt();
|
||||||
if (i < 0 || i >= frozenList.size()) return null;
|
|
||||||
return frozenList.get(i);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
if (i < 0 || i >= map.size()) return null;
|
|
||||||
|
|
||||||
if (i < map.size() / 2) {
|
|
||||||
var it = first;
|
|
||||||
for (var j = 0; j < i; it = it.next, j++);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var it = last;
|
|
||||||
for (var j = map.size() - 1; j >= i; it = it.prev, j--);
|
|
||||||
return it;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean has(String name) {
|
public boolean has(String name) {
|
||||||
return this.get(name) != null;
|
return this.map.containsKey(name);
|
||||||
}
|
}
|
||||||
|
|
||||||
public int size() {
|
public int size() {
|
||||||
@ -176,47 +167,38 @@ public final class VariableList implements Iterable<VariableDescriptor> {
|
|||||||
public void freeze() {
|
public void freeze() {
|
||||||
if (frozen()) return;
|
if (frozen()) return;
|
||||||
|
|
||||||
frozenMap = new HashMap<>();
|
|
||||||
frozenList = new ArrayList<>();
|
frozenList = new ArrayList<>();
|
||||||
|
|
||||||
for (var it = first; it != null; ) {
|
for (var node = first; node != null; ) {
|
||||||
frozenMap.put(it.name, it);
|
frozenList.add(node);
|
||||||
frozenList.add(it);
|
|
||||||
|
|
||||||
var tmp = it;
|
var tmp = node;
|
||||||
it = it.next;
|
node = node.next;
|
||||||
tmp.freeze();
|
tmp.freeze();
|
||||||
}
|
}
|
||||||
|
|
||||||
map = null;
|
|
||||||
first = last = null;
|
first = last = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override public Iterator<VariableDescriptor> iterator() {
|
public Iterable<Variable> all() {
|
||||||
if (frozen()) return frozenList.iterator();
|
if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator();
|
||||||
else return new Iterator<VariableDescriptor>() {
|
else return () -> new Iterator<Variable>() {
|
||||||
private ListVar curr = first;
|
private Node curr = first;
|
||||||
|
|
||||||
@Override public boolean hasNext() {
|
@Override public boolean hasNext() {
|
||||||
return curr != null;
|
return curr != null;
|
||||||
}
|
}
|
||||||
@Override public VariableDescriptor next() {
|
@Override public Variable next() {
|
||||||
if (curr == null) return null;
|
if (curr == null) return null;
|
||||||
|
|
||||||
var res = curr;
|
var res = curr;
|
||||||
curr = curr.next;
|
curr = curr.next;
|
||||||
return res;
|
return res.var;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
public Iterable<String> keys() {
|
||||||
public VariableDescriptor[] toArray() {
|
return () -> StreamSupport.stream(all().spliterator(), false).map(v -> v.name).iterator();
|
||||||
var res = new VariableDescriptor[size()];
|
|
||||||
var i = 0;
|
|
||||||
|
|
||||||
for (var el : this) res[i++] = el;
|
|
||||||
|
|
||||||
return res;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public VariableList setIndexMap(IntUnaryOperator map) {
|
public VariableList setIndexMap(IntUnaryOperator map) {
|
||||||
|
@ -35,7 +35,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
|
|
||||||
if (i == null) {
|
if (i == null) {
|
||||||
target.add(_i -> {
|
target.add(_i -> {
|
||||||
if (target.scope.has(name)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name));
|
if (target.scope.has(name, false)) throw new SyntaxException(loc(), String.format("Cannot access '%s' before initialization", name));
|
||||||
return Instruction.globGet(name);
|
return Instruction.globGet(name);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -50,7 +50,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
var i = target.scope.get(name, true);
|
var i = target.scope.get(name, true);
|
||||||
|
|
||||||
if (i == null) return _i -> {
|
if (i == null) return _i -> {
|
||||||
if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
|
if (target.scope.has(name, false)) throw new SyntaxException(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 -> Instruction.loadVar(i.index());
|
||||||
@ -64,7 +64,7 @@ public class VariableNode extends Node implements AssignableNode {
|
|||||||
var i = target.scope.get(name, true);
|
var i = target.scope.get(name, true);
|
||||||
|
|
||||||
if (i == null) return _i -> {
|
if (i == null) return _i -> {
|
||||||
if (target.scope.has(name)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
|
if (target.scope.has(name, false)) throw new SyntaxException(loc, String.format("Cannot access '%s' before initialization", name));
|
||||||
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"));
|
||||||
|
@ -45,7 +45,7 @@ const unwrapThis = (self, type, constr, name, arg, defaultVal) => {
|
|||||||
if (typeof self === type) return self;
|
if (typeof self === type) return self;
|
||||||
if (self instanceof constr && valueKey in self) self = self[valueKey];
|
if (self instanceof constr && valueKey in self) self = self[valueKey];
|
||||||
if (typeof self === type) return self;
|
if (typeof self === type) return self;
|
||||||
if (arguments.length > 5) return defaultVal;
|
if (defaultVal !== undefined) return defaultVal;
|
||||||
|
|
||||||
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
|
throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name);
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user