From 5b4adf52862dbb03d515c279f4e61d79b6d3470b Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Thu, 5 Sep 2024 00:28:13 +0300 Subject: [PATCH] a clusterfuck of fixes with let and const --- .../jscript/compilation/CompileResult.java | 39 +++- .../jscript/compilation/CompoundNode.java | 2 +- .../compilation/FunctionArrowNode.java | 7 +- .../jscript/compilation/FunctionNode.java | 136 ++++++------ .../compilation/FunctionStatementNode.java | 7 +- .../compilation/FunctionValueNode.java | 8 +- .../jscript/compilation/JavaScript.java | 44 ++-- .../compilation/VariableDeclareNode.java | 5 +- .../compilation/control/ForInNode.java | 5 +- .../jscript/compilation/control/ForNode.java | 2 +- .../compilation/control/ForOfNode.java | 5 +- .../compilation/control/SwitchNode.java | 2 +- .../jscript/compilation/control/TryNode.java | 3 +- .../compilation/scope/FunctionScope.java | 105 +++++++--- .../compilation/scope/GlobalScope.java | 33 --- .../jscript/compilation/scope/LocalScope.java | 69 ------- .../jscript/compilation/scope/Scope.java | 151 +++++++++++--- .../jscript/compilation/scope/Variable.java | 41 ++++ .../compilation/scope/VariableDescriptor.java | 19 -- .../compilation/scope/VariableList.java | 194 ++++++++---------- .../compilation/values/VariableNode.java | 6 +- src/main/resources/lib/index.js | 2 +- 22 files changed, 486 insertions(+), 399 deletions(-) delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 1c3d619..b68adc4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; +import java.util.function.Consumer; import java.util.function.IntFunction; 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.FunctionMapBuilder; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.scope.LocalScope; import me.topchetoeu.jscript.compilation.scope.Scope; 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> instructions; + // public final List> childrenTasks; public final List children; public final FunctionMapBuilder map; public final Environment env; public int length; + public Runnable buildTask = () -> { + throw new IllegalStateException("Compile result is not ready to be built"); + }; public final Scope scope; public int temp() { @@ -68,11 +82,16 @@ public final class CompileResult { setLocationAndDebug(instructions.size() - 1, loc, type); } - public CompileResult addChild(CompileResult child) { - this.children.add(child); - return child; + public int addChild(CompileResult res) { + this.children.add(res); + return this.children.size() - 1; } + // public int addChild(Supplier supplier) { + // this.childrenTasks.add(() -> supplier.get()); + // return childrenTasks.size() - 1; + // } + public Instruction[] instructions() { var res = new Instruction[instructions.size()]; var i = 0; @@ -106,15 +125,17 @@ public final class CompileResult { } 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 task) { this.scope = scope; - instructions = new ArrayList<>(); - children = new LinkedList<>(); - map = FunctionMap.builder(); + this.instructions = new ArrayList<>(); + this.children = new LinkedList<>(); + this.map = FunctionMap.builder(); this.env = env; + this.length = length; + this.buildTask = () -> task.accept(this); } private CompileResult(Scope scope, CompileResult parent) { this.scope = scope; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 19466c4..f89ebd6 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -43,7 +43,7 @@ public class CompoundNode extends Node { if (alloc) { subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); } if (!polluted && pollute) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index e6f72fa..851724b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -13,12 +13,9 @@ import me.topchetoeu.jscript.compilation.control.ReturnNode; public class FunctionArrowNode extends FunctionNode { @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) { - 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) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index d725326..06f6a3f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -3,12 +3,13 @@ package me.topchetoeu.jscript.compilation; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; 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.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; 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; 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) { - target.add(Instruction.loadFunc(id, true, true, false, name, captures)); + // protected void compileLoadFunc(CompileResult target, int id, int[] captures, String name) { + // 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) { - var env = target.env.child() + public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, boolean hasArgs, String _name, String selfName) { + var name = this.name() != null ? this.name() : _name; + + env = env.child() .remove(LabelContext.BREAK_CTX) .remove(LabelContext.CONTINUE_CTX); - var funcScope = new FunctionScope(target.scope); - var subtarget = new CompileResult(env, new LocalScope(funcScope)); + return new CompileResult(env, scope, params.params.size(), target -> { + if (hasArgs || params.params.size() > 0) target.add(Instruction.loadArgs()); - subtarget.length = params.params.size(); - - if (hasArgs || params.params.size() > 0) subtarget.add(Instruction.loadArgs()); - - 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 (hasArgs) { + var argsVar = scope.defineStrict(new Variable("arguments", true), loc()); + target.add(_i -> Instruction.storeVar(argsVar.index(), params.params.size() > 0)); } - } - if (params.restName != null) { - if (funcScope.hasArg(params.restName)) throw new SyntaxException(params.restLocation, "Duplicate parameter name not allowed"); - var restVar = funcScope.defineParam(params.restName, true, params.restLocation); - subtarget.add(Instruction.loadRestArgs(params.params.size())); - subtarget.add(_i -> Instruction.storeVar(restVar.index())); - } + if (params.params.size() > 0) { + if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1)); + var i = 0; - if (selfName != null && !funcScope.hasArg(name)) { - var i = funcScope.defineParam(selfName, true, end); + for (var param : params.params) { + 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()); - subtarget.add(_i -> Instruction.storeVar(i.index(), false)); - } + target.add(Instruction.loadMember(i++)); - body.resolve(subtarget); - body.compile(subtarget, false, false, BreakpointType.NONE); + if (param.node != null) { + var end = new DeferredIntSupplier(); - subtarget.scope.end(); - funcScope.end(); + target.add(Instruction.dup()); + 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 void compile(CompileResult target, boolean pollute, String name) { compile(target, pollute, name, BreakpointType.NONE); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 9d0398a..0c4be86 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -1,7 +1,9 @@ package me.topchetoeu.jscript.compilation; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class FunctionStatementNode extends FunctionNode { @@ -10,11 +12,12 @@ public class FunctionStatementNode extends FunctionNode { @Override public String name() { return name; } @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) { - 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)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index cfe0bf8..85f97a0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.compilation; +import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; @@ -8,8 +9,13 @@ public class FunctionValueNode extends FunctionNode { @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) { - 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) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index c5574d6..bd9b880 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -1,10 +1,9 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; +import java.util.List; 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.parsing.Filename; 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.TryNode; import me.topchetoeu.jscript.compilation.control.WhileNode; -import me.topchetoeu.jscript.compilation.scope.GlobalScope; -import me.topchetoeu.jscript.compilation.scope.LocalScope; +import me.topchetoeu.jscript.compilation.scope.FunctionScope; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; @@ -326,24 +324,32 @@ public final class JavaScript { } public static CompileResult compile(Environment env, Node ...statements) { - var target = new CompileResult(env, new LocalScope(new GlobalScope())); - var stm = new CompoundNode(null, statements); - var argsI = target.scope.defineStrict("arguments", true, null); - target.add(Instruction.loadArgs()); - target.add(_i -> Instruction.storeVar(argsI.index())); + var func = new FunctionValueNode(null, null, new Parameters(List.of()), new CompoundNode(null, statements), null); + var res = func.compileBody(env, new FunctionScope(true), true, true, null, null); + res.buildTask.run(); + return res; - // 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())); + // var target = new CompileResult(env, new FunctionScope(true)); + // var stm = ; + // var argsI = target.scope.defineStrict(new Variable("arguments", true), null); + // target.add(Instruction.loadArgs()); + // target.add(_i -> Instruction.storeVar(argsI.index())); - // 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) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 24ec36b..c35dffd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableDeclareNode extends Node { @@ -31,7 +32,7 @@ public class VariableDeclareNode extends Node { @Override public void resolve(CompileResult target) { if (!declType.strict) { 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) { for (var entry : values) { 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) { FunctionNode.compileWithName(entry.value, target, true, entry.name, BreakpointType.STEP_OVER); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java index 4a8e4d5..7c65d31 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForInNode extends Node { @@ -24,11 +25,11 @@ public class ForInNode extends Node { @Override public void resolve(CompileResult 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) { - 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); target.add(Instruction.keys(true)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java index e0d125c..f09148b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -47,7 +47,7 @@ public class ForNode extends Node { if (pollute) subtarget.add(Instruction.pushUndefined()); 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) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java index 6cc1aa9..9f55541 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; +import me.topchetoeu.jscript.compilation.scope.Variable; import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForOfNode extends Node { @@ -23,11 +24,11 @@ public class ForOfNode extends Node { @Override public void resolve(CompileResult 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) { - 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); target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index f41a997..a48f432 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -65,7 +65,7 @@ public class SwitchNode extends Node { LabelContext.getBreak(target.env).pop(label); subtarget.scope.end(); - subtarget.add(Instruction.stackFree(subtarget.scope.allocCount())); + subtarget.add(_i -> Instruction.stackFree(subtarget.scope.allocCount())); int endI = subtarget.size(); end.set(endI); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java index 04e4650..b972a1a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -12,6 +12,7 @@ import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.Node; +import me.topchetoeu.jscript.compilation.scope.Variable; public class TryNode extends Node { public final CompoundNode tryBody; @@ -42,7 +43,7 @@ public class TryNode extends Node { if (captureName != null) { var subtarget = target.subtarget(); - subtarget.scope.defineStrict(captureName, false, catchBody.loc()); + subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc()); catchBody.compile(subtarget, false); subtarget.scope.end(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java index d20a277..ee3444d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -1,79 +1,113 @@ package me.topchetoeu.jscript.compilation.scope; import java.util.HashMap; +import java.util.HashSet; import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.runtime.exceptions.SyntaxException; public class FunctionScope extends Scope { private final VariableList captures = new VariableList().setIndexMap(v -> ~v); private final VariableList specials = new VariableList(); private final VariableList locals = new VariableList(specials); - private HashMap childToParent = new HashMap<>(); + private final HashMap childToParent = new HashMap<>(); + private final HashSet blacklistNames = new HashSet<>(); + + private final Scope captureParent; + + public final boolean passtrough; private void removeCapture(String 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) { - var old = locals.get(name); - if (old != null) return old; + @Override public Variable define(Variable var, Location loc) { + checkNotEnded(); + 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); - return locals.add(name, readonly); + if (passtrough) { + blacklistNames.add(var.name); + return null; + } + + removeCapture(var.name); + return locals.add(var); } - @Override public VariableDescriptor defineStrict(String name, boolean readonly, Location loc) { - if (locals.has(name)) throw alreadyDefinedErr(loc, name); - else if (parent == null) throw new RuntimeException("Strict variables may be defined only in local scopes"); - else return parent.defineStrict(name, readonly, loc); + @Override public Variable defineStrict(Variable var, Location loc) { + checkNotEnded(); + 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); + + var res = super.defineStrict(var, loc); + removeCapture(var.name); + return res; } - public VariableDescriptor defineParam(String name, boolean readonly, Location loc) { - return specials.add(name, readonly); - } - public boolean hasArg(String name) { - return specials.has(name); + public Variable defineParam(Variable var, Location loc) { + return specials.add(var); } - @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 (locals.has(name)) return locals.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; - var childVar = captures.add(parentVar); + var childVar = captures.add(parentVar.clone()); childToParent.put(childVar, parentVar); return childVar; } - @Override public boolean has(String name) { + @Override public boolean has(String name, boolean capture) { if (specials.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; } - @Override public boolean end() { - if (!super.end()) return false; + @Override public boolean finish() { + if (!super.finish()) return false; captures.freeze(); locals.freeze(); + specials.freeze(); + return true; } - @Override public int localsCount() { - return locals.size() + specials.size(); + @Override public int allocCount() { + return 0; } @Override public int capturesCount() { return captures.size(); } - @Override public int allocCount() { - return 0; + @Override public int localsCount() { + return locals.size() + specials.size() + super.allocCount(); } public int offset() { @@ -84,7 +118,7 @@ public class FunctionScope extends Scope { var res = new int[captures.size()]; var i = 0; - for (var el : captures) { + for (var el : captures.all()) { assert childToParent.containsKey(el); res[i] = childToParent.get(el).index(); i++; @@ -93,10 +127,15 @@ public class FunctionScope extends Scope { return res; } - public FunctionScope() { - super(); - } 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; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java deleted file mode 100644 index 6151a8e..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/GlobalScope.java +++ /dev/null @@ -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(); } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java deleted file mode 100644 index 71599f7..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/LocalScope.java +++ /dev/null @@ -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 all() { - return () -> locals.iterator(); - } - - - public LocalScope(Scope parent) { - super(parent); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java index c41c2bd..7846efd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -1,24 +1,60 @@ 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.runtime.exceptions.SyntaxException; -public abstract class Scope { - public final Scope parent; - private boolean active = true; +public class Scope { + protected final VariableList variables = new VariableList(this::parentOffset); + + private boolean ended = false; + private boolean finished = false; private Scope child; + private List prevChildren = new LinkedList<>(); + + public final Scope parent; + public final HashSet 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) { 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 + * * @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 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 * @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 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 * - * @param capture Used to signal to the scope that the variable is going to be captured. - * 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 + * @param capture If true, the variable is being captured by a function */ - public abstract VariableDescriptor get(String name, boolean capture); - public abstract boolean has(String name); + public Variable get(String name, boolean capture) { + 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 */ - 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(); - public abstract int allocCount(); + /** + * Adds this variable to the current function's locals record. Capturable indicates whether or not the variable + * should still be capturable, or be put in an array (still not implemented) + * + * @return Whether or not the request was actually fuliflled + */ + public boolean flattenVariable(Variable variable, boolean capturable) { + if (parent == null) return false; + return parent.flattenVariable(variable, capturable); + } - public boolean end() { - if (!active) return false; + public int localsCount() { return 0; } + 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) { assert this.parent.child == this; this.parent.child = null; @@ -57,17 +140,39 @@ public abstract class Scope { 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 Scope() { - this.parent = null; + this(null); } public Scope(Scope parent) { - if (!parent.active) throw new RuntimeException("Parent is not active"); - if (parent.child != null) throw new RuntimeException("Parent has an active child"); + if (parent != null) { + 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.child = this; + this.parent = parent; + this.parent.child = this; + this.parent.prevChildren.add(this); + } + else this.parent = null; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java new file mode 100644 index 0000000..af74088 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java @@ -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); + } +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java deleted file mode 100644 index 29b3bb5..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableDescriptor.java +++ /dev/null @@ -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; } - }; - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java index 036c313..c67859d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -5,15 +5,17 @@ import java.util.HashMap; import java.util.Iterator; import java.util.function.IntSupplier; import java.util.function.IntUnaryOperator; +import java.util.stream.StreamSupport; -public final class VariableList implements Iterable { - private class ListVar extends VariableDescriptor { - private ListVar next; - private ListVar prev; - private boolean frozen; - private int index; +public final class VariableList { + private final class Node implements IntSupplier { + public Variable var; + public Node next; + public Node prev; + public boolean frozen; + public int index; - @Override public int index() { + @Override public int getAsInt() { if (frozen) { if (offset == null) { return indexConverter == null ? index : indexConverter.applyAsInt(index); @@ -35,41 +37,38 @@ public final class VariableList implements Iterable { return indexConverter == null ? res : indexConverter.applyAsInt(res); } - public ListVar freeze() { - if (frozen) return this; + public void freeze() { + if (frozen) return; this.frozen = true; this.next = null; - if (prev == null) return this; + this.var.freeze(); + if (prev == null) return; this.index = prev.index + 1; this.next = null; - return this; + return; } - public ListVar(String name, boolean readonly, ListVar next, ListVar prev) { - super(name, readonly); - + public Node(Variable var, Node next, Node prev) { + this.var = var; this.next = next; this.prev = prev; } } - private ListVar first, last; + private Node first, last; - private HashMap map = new HashMap<>(); - - private HashMap frozenMap = null; - private ArrayList frozenList = null; + private final HashMap map = new HashMap<>(); + private ArrayList frozenList = null; private final IntSupplier offset; private IntUnaryOperator indexConverter = null; public boolean frozen() { - if (frozenMap != null) { + if (frozenList != null) { assert frozenList != null; - assert frozenMap != null; - assert map == null; + assert map != null; assert first == null; assert last == null; @@ -77,95 +76,87 @@ public final class VariableList implements Iterable { } else { assert frozenList == null; - assert frozenMap == null; assert map != null; return false; } } - public VariableDescriptor add(VariableDescriptor val) { - 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) { + private Variable add(Variable val, boolean overlay) { 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); - if (el == null) return null; + var node = new Node(val, null, last); - if (el.prev != null) { - assert el != first; - el.prev.next = el.next; + if (last != null) { + assert first != null; + + last.next = node; + node.prev = last; + + last = node; } 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; } - if (el.next != null) { - assert el != last; - el.next.prev = el.prev; + if (node.next != null) { + assert node != last; + node.next.prev = node.prev; } else { - assert el == last; + assert node == last; last = last.prev; } - el.next = null; - el.prev = null; + node.next = null; + node.prev = null; - return el; + return node.var; } - public VariableDescriptor get(String name) { - if (frozen()) return frozenMap.get(name); - else return map.get(name); + public Variable get(String name) { + var res = map.get(name); + if (res != null) return res.var; + else return null; } - public VariableDescriptor get(int i) { - if (frozen()) { - 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 int indexOfKey(String name) { + return map.get(name).getAsInt(); } public boolean has(String name) { - return this.get(name) != null; + return this.map.containsKey(name); } public int size() { @@ -176,47 +167,38 @@ public final class VariableList implements Iterable { public void freeze() { if (frozen()) return; - frozenMap = new HashMap<>(); frozenList = new ArrayList<>(); - for (var it = first; it != null; ) { - frozenMap.put(it.name, it); - frozenList.add(it); + for (var node = first; node != null; ) { + frozenList.add(node); - var tmp = it; - it = it.next; + var tmp = node; + node = node.next; tmp.freeze(); } - map = null; first = last = null; } - @Override public Iterator iterator() { - if (frozen()) return frozenList.iterator(); - else return new Iterator() { - private ListVar curr = first; + public Iterable all() { + if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator(); + else return () -> new Iterator() { + private Node curr = first; @Override public boolean hasNext() { return curr != null; } - @Override public VariableDescriptor next() { + @Override public Variable next() { if (curr == null) return null; var res = curr; curr = curr.next; - return res; + return res.var; } }; } - - public VariableDescriptor[] toArray() { - var res = new VariableDescriptor[size()]; - var i = 0; - - for (var el : this) res[i++] = el; - - return res; + public Iterable keys() { + return () -> StreamSupport.stream(all().spliterator(), false).map(v -> v.name).iterator(); } public VariableList setIndexMap(IntUnaryOperator map) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java index bca18a2..1ea10ad 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -35,7 +35,7 @@ public class VariableNode extends Node implements AssignableNode { if (i == null) { 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); }); @@ -50,7 +50,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); 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 _i -> Instruction.loadVar(i.index()); @@ -64,7 +64,7 @@ public class VariableNode extends Node implements AssignableNode { var i = target.scope.get(name, true); 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 if (!define && i.readonly) return _i -> Instruction.throwSyntax(new SyntaxException(loc, "Assignment to constant variable")); diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index 1c34848..c807527 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -45,7 +45,7 @@ const unwrapThis = (self, type, constr, name, arg, defaultVal) => { if (typeof self === type) return self; if (self instanceof constr && valueKey in self) self = self[valueKey]; 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); }