From fdac93bf4dbb01f92a0fbaeea7cc1877ea2cd68f Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 20 Sep 2024 10:16:48 +0300 Subject: [PATCH 01/41] fix: scope offsets calculated incorrectly --- .../java/me/topchetoeu/jscript/compilation/scope/Scope.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) 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 bed8f93..62de6c0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -129,7 +129,7 @@ public class Scope { var res = 0; for (var curr = parent; curr != null; curr = curr.parent) { - res += parent.locals.size(); + res += curr.locals.size(); } return res; @@ -138,7 +138,7 @@ public class Scope { var res = 0; for (var curr = this; curr != null; curr = curr.parent) { - if (curr != this) res += parent.capturables.size(); + if (curr != this) res += curr.capturables.size(); if (curr.parent == null) res += curr.localsCount(); } -- 2.45.2 From 59e6f34a01013f58061c1511f6e362686756f634 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:37:13 +0300 Subject: [PATCH 02/41] refactor: clean up REPL stringification code --- .../jscript/runtime/values/Value.java | 106 +----------------- .../values/functions/FunctionValue.java | 20 ++++ .../values/objects/ArrayLikeValue.java | 90 +++++++++++++++ .../runtime/values/objects/ObjectValue.java | 80 +++++++++++++ .../values/primitives/StringValue.java | 9 ++ .../runtime/values/primitives/VoidValue.java | 8 ++ .../values/primitives/numbers/IntValue.java | 11 ++ 7 files changed, 224 insertions(+), 100 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index 0604cdd..2979392 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -3,27 +3,25 @@ package me.topchetoeu.jscript.runtime.values; import java.io.ByteArrayOutputStream; import java.io.PrintStream; import java.util.ArrayList; +import java.util.Arrays; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashSet; +import java.util.List; import java.util.Map; import java.util.Set; import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.runtime.EventLoop; -import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.functions.NativeFunction; -import me.topchetoeu.jscript.runtime.values.objects.ArrayValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.BoolValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; @@ -429,105 +427,13 @@ public abstract class Value { } } - private final String toReadable(Environment env, HashSet passed, int tab) { - if (passed.contains(this)) return "[circular]"; - - if (this instanceof ObjectValue obj) { - var res = new StringBuilder(); - var dbg = DebugContext.get(env); - var printed = true; - var keys = this.getMembers(env, true, false); - - if (this instanceof FunctionValue func) { - res.append(this.toString()); - var loc = dbg.getMapOrEmpty(func).start(); - - if (loc != null) res.append(" @ " + loc); - - if ( - func.prototype instanceof ObjectValue objProto && - objProto.getMember(env, "constructor") == func && - objProto.getOwnMembers(env, true).size() + objProto.getOwnSymbolMembers(env, true).size() == 1 - ) { keys.remove("constructor"); } - } - else if (this instanceof ArrayValue) { - res.append("["); - var arr = (ArrayValue)this; - - for (int i = 0; i < arr.size(); i++) { - if (i != 0) res.append(", "); - else res.append(" "); - - if (arr.hasMember(env, i, true)) { - res.append(arr.getMember(env, i).toReadable(env, passed, tab)); - keys.remove(i + ""); - } - else res.append(""); - } - - res.append(" ] "); - } - else printed = false; - - - passed.add(this); - - if (keys.size() + obj.getOwnSymbolMembers(env, true).size() == 0) { - if (!printed) res.append("{}"); - } - else if (!printed) { - if (tab > 3) return "{...}"; - res.append("{\n"); - - for (var entry : obj.getOwnSymbolMembers(env, true)) { - for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append("[" + entry.value + "]" + ": "); - - var member = obj.getOwnMember(env, entry); - if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1)); - else if (member instanceof PropertyMember prop) { - if (prop.getter == null && prop.setter == null) res.append("[No accessors]"); - else if (prop.getter == null) res.append("[Setter]"); - else if (prop.setter == null) res.append("[Getter]"); - else res.append("[Getter/Setter]"); - } - else res.append("[???]"); - - res.append(",\n"); - } - for (var entry : obj.getOwnMembers(env, true)) { - for (int i = 0; i < tab + 1; i++) res.append(" "); - res.append(entry + ": "); - - var member = obj.getOwnMember(env, entry); - if (member instanceof FieldMember field) res.append(field.get(env, obj).toReadable(env, passed, tab + 1)); - else if (member instanceof PropertyMember prop) { - if (prop.getter == null && prop.setter == null) res.append("[No accessors]"); - else if (prop.getter == null) res.append("[Setter]"); - else if (prop.setter == null) res.append("[Getter]"); - else res.append("[Getter/Setter]"); - } - else res.append("[???]"); - - res.append(",\n"); - } - - for (int i = 0; i < tab; i++) res.append(" "); - res.append("}"); - } - - passed.remove(this); - return res.toString(); - } - else if (this instanceof VoidValue) return ((VoidValue)this).name; - else if (this instanceof StringValue) return JSON.stringify(JSONElement.string(((StringValue)this).value)); - else if (this instanceof SymbolValue) return this.toString(); - else if (this instanceof NumberValue num && num.isLong()) return num.getLong() + "i"; - else return this.toString(env); + /** @internal */ + public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(toString(env)); } public final String toReadable(Environment ext) { - return toReadable(ext, new HashSet<>(), 0); + return String.join("\n", toReadableLines(ext, new HashSet<>())); } public static final ObjectValue global(Environment env) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 7e49161..6bfbb8d 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -1,6 +1,12 @@ package me.topchetoeu.jscript.runtime.values.functions; +import java.util.Arrays; +import java.util.HashSet; +import java.util.LinkedList; +import java.util.List; + import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; @@ -83,6 +89,20 @@ public abstract class FunctionValue extends ObjectValue { @Override public StringValue type() { return StringValue.of("function"); } + @Override public List toReadableLines(Environment env, HashSet passed) { + var dbg = DebugContext.get(env); + var res = new StringBuilder(this.toString()); + var loc = dbg.getMapOrEmpty(this).start(); + + if (loc != null) res.append(" @ " + loc); + + var lines = new LinkedList(super.toReadableLines(env, passed)); + if (lines.size() == 1 && lines.getFirst().equals("{}")) return Arrays.asList(res.toString()); + lines.set(0, res.toString() + " " + lines.getFirst()); + + return lines; + } + public void setName(String val) { if (this.name == null || this.name.equals("")) this.name = val; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayLikeValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayLikeValue.java index 4b39653..6de8e2f 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayLikeValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ArrayLikeValue.java @@ -1,6 +1,10 @@ package me.topchetoeu.jscript.runtime.values.objects; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; @@ -104,4 +108,90 @@ public abstract class ArrayLikeValue extends ObjectValue { return res; } + + private LinkedList toReadableBase(Environment env, HashSet passed, HashSet ignoredKeys) { + var stringified = new LinkedList>(); + + passed.add(this); + + var emptyN = 0; + + for (int i = 0; i < size(); i++) { + if (has(i)) { + String emptyStr = null; + + if (emptyN == 1) emptyStr = ""; + else if (emptyN > 1) emptyStr = ""; + + if (emptyStr != null) stringified.add(new LinkedList<>(Arrays.asList(emptyStr + ","))); + emptyN = 0; + + stringified.add(new LinkedList<>(get(i).toReadableLines(env, passed))); + ignoredKeys.add(i + ""); + + var entry = stringified.getLast(); + entry.set(entry.size() - 1, entry.getLast() + ","); + } + else { + emptyN++; + } + } + + String emptyStr = null; + + if (emptyN == 1) emptyStr = ""; + else if (emptyN > 1) emptyStr = ""; + + if (emptyStr != null) stringified.add(new LinkedList<>(Arrays.asList(emptyStr))); + else if (stringified.size() > 0) { + var lastEntry = stringified.getLast(); + lastEntry.set(lastEntry.size() - 1, lastEntry.getLast().substring(0, lastEntry.getLast().length() - 1)); + } + + + passed.remove(this); + + if (stringified.size() == 0) return new LinkedList<>(Arrays.asList("[]")); + var concat = new StringBuilder(); + for (var entry : stringified) { + // We make a one-liner only when all members are one-liners + if (entry.size() != 1) { + concat = null; + break; + } + + if (concat.length() != 0) concat.append(" "); + concat.append(entry.get(0)); + } + + // We don't want too long one-liners + if (concat != null && concat.length() < 160) return new LinkedList<>(Arrays.asList("[" + concat.toString() + "]")); + + var res = new LinkedList(); + + res.add("["); + + for (var entry : stringified) { + for (var line : entry) { + res.add(" " + line); + } + } + res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1)); + res.add("]"); + + return res; + } + + @Override public List toReadableLines(Environment env, HashSet passed) { + var ignored = new HashSet(); + var lines = toReadableBase(env, passed, ignored); + + var superLines = new LinkedList(super.toReadableLines(env, passed, ignored)); + if (superLines.size() == 1 && superLines.getFirst().equals("{}")) return lines; + + lines.set(lines.size() - 1, lines.getLast() + " " + superLines.getFirst()); + lines.addAll(superLines.subList(1, superLines.size())); + + return lines; + } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index c492bbe..616b775 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -1,7 +1,11 @@ package me.topchetoeu.jscript.runtime.values.objects; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashMap; import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Set; import me.topchetoeu.jscript.common.environment.Environment; @@ -10,6 +14,7 @@ import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.functions.FunctionValue; import me.topchetoeu.jscript.runtime.values.primitives.StringValue; import me.topchetoeu.jscript.runtime.values.primitives.SymbolValue; @@ -134,6 +139,81 @@ public class ObjectValue extends Value { return setPrototype(_env -> val); } + private final LinkedList memberToReadable(Environment env, String key, Member member, HashSet passed) { + if (member instanceof PropertyMember prop) { + if (prop.getter == null && prop.setter == null) return new LinkedList<>(Arrays.asList(key + ": [No accessors]")); + else if (prop.getter == null) return new LinkedList<>(Arrays.asList(key + ": [Setter]")); + else if (prop.setter == null) return new LinkedList<>(Arrays.asList(key + ": [Getter]")); + else return new LinkedList<>(Arrays.asList(key + ": [Getter/Setter]")); + } + else { + var res = new LinkedList(); + var first = true; + + for (var line : member.get(env, this).toReadableLines(env, passed)) { + if (first) res.add(key + ": " + line); + else res.add(line); + first = false; + } + + return res; + } + } + + public List toReadableLines(Environment env, HashSet passed, HashSet ignoredKeys) { + passed.add(this); + + var stringified = new LinkedList>(); + + for (var entry : getOwnSymbolMembers(env, true)) { + var member = getOwnMember(env, entry); + stringified.add(memberToReadable(env, "[" + entry.value + "]", member, passed)); + } + for (var entry : getOwnMembers(env, true)) { + if (ignoredKeys.contains(entry)) continue; + + var member = getOwnMember(env, entry); + stringified.add(memberToReadable(env, entry, member, passed)); + } + + passed.remove(this); + + if (stringified.size() == 0) return Arrays.asList("{}"); + var concat = new StringBuilder(); + for (var entry : stringified) { + // We make a one-liner only when all members are one-liners + if (entry.size() != 1) { + concat = null; + break; + } + + if (concat.length() != 0) concat.append(", "); + concat.append(entry.get(0)); + } + + // We don't want too long one-liners + if (concat != null && concat.length() < 80) return Arrays.asList("{ " + concat.toString() + " }"); + + var res = new LinkedList(); + + res.add("{"); + + for (var entry : stringified) { + for (var line : entry) { + res.add(" " + line); + } + + res.set(res.size() - 1, res.getLast() + ","); + } + res.set(res.size() - 1, res.getLast().substring(0, res.getLast().length() - 1)); + res.add("}"); + + return res; + } + @Override public List toReadableLines(Environment env, HashSet passed) { + return toReadableLines(env, passed, new HashSet<>()); + } + public final boolean setPrototype(PrototypeProvider val) { if (!getState().extendable) return false; prototype = val; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index c715c51..7789e06 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -1,10 +1,15 @@ package me.topchetoeu.jscript.runtime.values.primitives; +import java.util.Arrays; +import java.util.HashSet; import java.util.LinkedHashSet; +import java.util.List; import java.util.Set; import java.util.WeakHashMap; import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.json.JSON; +import me.topchetoeu.jscript.common.json.JSONElement; import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.runtime.values.KeyCache; @@ -67,6 +72,10 @@ public final class StringValue extends PrimitiveValue { return res; } + @Override public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(JSON.stringify(JSONElement.string(value))); + } + private StringValue(String value) { this.value = value; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 0949ef3..07c53d6 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -1,5 +1,9 @@ package me.topchetoeu.jscript.runtime.values.primitives; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; @@ -22,6 +26,10 @@ public final class VoidValue extends PrimitiveValue { throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); } + @Override public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(name); + } + public VoidValue(String name, String type) { this.name = name; this.type = type; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java index 9d90a75..5124c1b 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java @@ -1,5 +1,12 @@ package me.topchetoeu.jscript.runtime.values.primitives.numbers; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; + +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; + public final class IntValue extends NumberValue { public final long value; @@ -26,6 +33,10 @@ public final class IntValue extends NumberValue { else return false; } + @Override public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(value + "i"); + } + public IntValue(long value) { this.value = value; } -- 2.45.2 From 8dee4353d4948fa0ada3efa7d50060ce8cfc55e8 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:37:42 +0300 Subject: [PATCH 03/41] feat: implement non-enumerable members in classes --- .../jscript/common/Instruction.java | 8 +- .../jscript/compilation/ClassNode.java | 25 ++-- .../jscript/compilation/FunctionNode.java | 6 +- .../jscript/compilation/JavaScript.java | 8 ++ .../members/AssignShorthandNode.java | 9 +- .../compilation/members/FieldMemberNode.java | 11 +- .../jscript/compilation/members/Member.java | 10 ++ .../compilation/members/MethodMemberNode.java | 7 +- .../members/PropertyMemberNode.java | 8 +- .../compilation/values/ObjectNode.java | 11 +- .../jscript/compilation/values/SuperNode.java | 17 +++ .../compilation/values/VariableNode.java | 6 +- .../jscript/runtime/InstructionRunner.java | 7 +- src/main/resources/lib/index.js | 129 ++++++++---------- 14 files changed, 151 insertions(+), 111 deletions(-) create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/Member.java create mode 100644 src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index fa441f5..f4884ae 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -454,11 +454,11 @@ public class Instruction { return new Instruction(Type.KEYS, own, onlyEnumerable); } - public static Instruction defProp(boolean setter) { - return new Instruction(Type.DEF_PROP, setter); + public static Instruction defProp(boolean setter, boolean enumerable) { + return new Instruction(Type.DEF_PROP, setter, enumerable); } - public static Instruction defField() { - return new Instruction(Type.DEF_FIELD); + public static Instruction defField(boolean enumerable) { + return new Instruction(Type.DEF_FIELD, enumerable); } public static Instruction operation(Operation op) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java index 9b2ba98..898009a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java @@ -11,19 +11,20 @@ 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.members.FieldMemberNode; +import me.topchetoeu.jscript.compilation.members.Member; import me.topchetoeu.jscript.compilation.members.MethodMemberNode; import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; public abstract class ClassNode extends FunctionNode { public static final class ClassBody { - public final List staticMembers; + public final List staticMembers; public final List protoFields; - public final List protoMembers; + public final List protoMembers; public final Parameters constructorParameters; public final CompoundNode constructorBody; public ClassBody( - List staticMembers, List protoFields, List protoMembers, + List staticMembers, List protoFields, List protoMembers, Parameters constructorParameters, CompoundNode constructorBody ) { this.staticMembers = staticMembers; @@ -34,13 +35,15 @@ public abstract class ClassNode extends FunctionNode { } } + // public static final Key + public final ClassBody body; public final String name; @Override public String name() { return name; } public void compileStatic(CompileResult target) { - for (var member : body.staticMembers) member.compile(target, true); + for (var member : body.staticMembers) member.compile(target, true, false); } public void compilePrototype(CompileResult target) { if (body.protoMembers.size() > 0) { @@ -48,17 +51,17 @@ public abstract class ClassNode extends FunctionNode { target.add(Instruction.loadMember("prototype")); for (var i = 0; i < body.protoMembers.size() - 1; i++) { - body.protoMembers.get(i).compile(target, true); + body.protoMembers.get(i).compile(target, true, false); } - body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false); + body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false, false); } } @Override protected void compilePreBody(CompileResult target) { for (var member : body.protoFields) { target.add(Instruction.loadThis()); - member.compile(target, false); + member.compile(target, false, false); } } @@ -76,7 +79,7 @@ public abstract class ClassNode extends FunctionNode { this.body = body; } - public static ParseRes parseMember(Source src, int i) { + public static ParseRes parseMember(Source src, int i) { return ParseRes.first(src, i, PropertyMemberNode::parse, FieldMemberNode::parseClass, @@ -93,8 +96,8 @@ public abstract class ClassNode extends FunctionNode { n += Parsing.skipEmpty(src, i + n); var fields = new LinkedList(); - var members = new LinkedList(); - var statics = new LinkedList(); + var members = new LinkedList(); + var statics = new LinkedList(); var params = new Parameters(new ArrayList<>()); var body = new CompoundNode(loc, false); @@ -106,7 +109,7 @@ public abstract class ClassNode extends FunctionNode { } while (true) { - ParseRes prop = parseMember(src, i + n); + ParseRes prop = parseMember(src, i + n); if (prop.isSuccess()) { n += prop.n; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 270ea0d..730cf1f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -27,10 +27,6 @@ public abstract class FunctionNode extends Node { public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { var name = this.name() != null ? this.name() : _name; - env = env.child() - .remove(LabelContext.BREAK_CTX) - .remove(LabelContext.CONTINUE_CTX); - return new CompileResult(env, scope, params.params.size(), target -> { compilePreBody(target); @@ -68,7 +64,7 @@ public abstract class FunctionNode extends Node { }); } public final CompileResult compileBody(CompileResult parent, String name, String selfName) { - return compileBody(parent.env, new FunctionScope(parent.scope), false, name, selfName); + return compileBody(parent.env.get(JavaScript.COMPILE_ROOT).child(), new FunctionScope(parent.scope), false, name, selfName); } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index cc0441d..d9ddbc9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -7,6 +7,7 @@ import java.util.Set; import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -31,6 +32,7 @@ import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ClassValueNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; +import me.topchetoeu.jscript.compilation.values.SuperNode; import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode; @@ -59,6 +61,8 @@ public final class JavaScript { } } + public static final Key COMPILE_ROOT = Key.of(); + static final Set reserved = new HashSet<>(Arrays.asList( "true", "false", "void", "null", "this", "if", "else", "try", "catch", "finally", "for", "do", "while", "switch", "case", "default", "new", @@ -120,6 +124,7 @@ public final class JavaScript { if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); + if (id.result.equals("super")) return ParseRes.res(new SuperNode(loc), n); if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); return ParseRes.failed(); @@ -264,6 +269,9 @@ public final class JavaScript { } public static CompileResult compile(Environment env, Node ...statements) { + env = env.child(); + env.add(COMPILE_ROOT, env); + var func = new FunctionValueNode(null, null, new Parameters(Arrays.asList()), new CompoundNode(null, true, statements), null); var res = func.compileBody(env, new FunctionScope(true), true, null, null); res.buildTask.run(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java index 5fc491c..116f787 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java @@ -13,17 +13,20 @@ import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; import me.topchetoeu.jscript.compilation.values.operations.AssignNode; -public class AssignShorthandNode extends Node { +public class AssignShorthandNode implements Member { + public final Location loc; public final Node key; public final AssignTarget target; public final Node value; - @Override public void compile(CompileResult target, boolean pollute) { + @Override public Location loc() { return loc; } + + @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context"); } public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) { - super(loc); + this.loc = loc; this.key = key; this.target = target; this.value = value; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java index 9706722..4402693 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -12,22 +12,25 @@ import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class FieldMemberNode extends Node { +public class FieldMemberNode implements Member { + public final Location loc; public final Node key; public final Node value; - @Override public void compile(CompileResult target, boolean pollute) { + @Override public Location loc() { return loc; } + + @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { if (pollute) target.add(Instruction.dup()); key.compile(target, true); if (value == null) target.add(Instruction.pushUndefined()); else value.compile(target, true); - target.add(Instruction.defField()); + target.add(Instruction.defField(enumerable)); } public FieldMemberNode(Location loc, Node key, Node value) { - super(loc); + this.loc = loc; this.key = key; this.value = value; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java new file mode 100644 index 0000000..505071a --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java @@ -0,0 +1,10 @@ +package me.topchetoeu.jscript.compilation.members; + +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; + +public interface Member { + Location loc(); + + void compile(CompileResult target, boolean pollute, boolean enumerable); +} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java index 8636c3d..d100366 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java @@ -14,7 +14,7 @@ import me.topchetoeu.jscript.compilation.Parameters; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class MethodMemberNode extends FunctionNode { +public class MethodMemberNode extends FunctionNode implements Member { public final Node key; @Override public String name() { @@ -28,8 +28,11 @@ public class MethodMemberNode extends FunctionNode { var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); + } - target.add(Instruction.defField()); + @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { + compile(target, pollute); + target.add(Instruction.defField(enumerable)); } public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java index 6e49e62..ff7d748 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -17,7 +17,7 @@ import me.topchetoeu.jscript.compilation.patterns.Pattern; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class PropertyMemberNode extends FunctionNode { +public class PropertyMemberNode extends FunctionNode implements Member{ public final Node key; public final Pattern argument; @@ -38,8 +38,12 @@ public class PropertyMemberNode extends FunctionNode { var id = target.addChild(compileBody(target, name, null)); target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); + } - target.add(Instruction.defProp(isSetter())); + + @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { + compile(target, pollute); + target.add(Instruction.defProp(isSetter(), enumerable)); } public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index eee8f17..b694117 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -14,6 +14,7 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.members.AssignShorthandNode; import me.topchetoeu.jscript.compilation.members.FieldMemberNode; +import me.topchetoeu.jscript.compilation.members.Member; import me.topchetoeu.jscript.compilation.members.MethodMemberNode; import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; import me.topchetoeu.jscript.compilation.patterns.AssignTarget; @@ -23,7 +24,7 @@ import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class ObjectNode extends Node implements AssignTargetLike { - public final List members; + public final List members; // TODO: Implement spreading into object @@ -64,7 +65,7 @@ public class ObjectNode extends Node implements AssignTargetLike { @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); - for (var el : members) el.compile(target, true); + for (var el : members) el.compile(target, true, true); } @Override public AssignTarget toAssignTarget() { @@ -82,7 +83,7 @@ public class ObjectNode extends Node implements AssignTargetLike { return new ObjectPattern(loc(), newMembers); } - public ObjectNode(Location loc, List map) { + public ObjectNode(Location loc, List map) { super(loc); this.members = map; } @@ -126,7 +127,7 @@ public class ObjectNode extends Node implements AssignTargetLike { n++; n += Parsing.skipEmpty(src, i + n); - var members = new LinkedList(); + var members = new LinkedList(); if (src.is(i + n, "}")) { n++; @@ -134,7 +135,7 @@ public class ObjectNode extends Node implements AssignTargetLike { } while (true) { - ParseRes prop = ParseRes.first(src, i + n, + ParseRes prop = ParseRes.first(src, i + n, MethodMemberNode::parse, PropertyMemberNode::parse, FieldMemberNode::parseObject, diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java new file mode 100644 index 0000000..b7284fb --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java @@ -0,0 +1,17 @@ +package me.topchetoeu.jscript.compilation.values; + +import me.topchetoeu.jscript.common.SyntaxException; +import me.topchetoeu.jscript.common.parsing.Location; +import me.topchetoeu.jscript.compilation.CompileResult; +import me.topchetoeu.jscript.compilation.Node; + + +public class SuperNode extends Node { + @Override public void compile(CompileResult target, boolean pollute) { + throw new SyntaxException(loc(), "Unexpected 'super' reference here"); + } + + public SuperNode(Location loc) { + super(loc); + } +} 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 ae32c9c..c4e90e4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -45,6 +45,8 @@ public class VariableNode extends Node implements Pattern, ChangeTarget { if (i == null) return Instruction.globDef(name); else return Instruction.nop(); }); + + target.setLocation(loc()); } @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { @@ -57,10 +59,12 @@ public class VariableNode extends Node implements Pattern, ChangeTarget { var v = target.scope.define(decl, name, loc()); target.add(_i -> v.index().toInit()); } + + target.setLocation(loc()); } @Override public void compile(CompileResult target, boolean pollute) { - target.add(toGet(target, loc(), name, true, false)); + target.add(toGet(target, loc(), name, true, false)).setLocation(loc()); } public static IntFunction toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index a019369..8bdc6dd 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -7,6 +7,7 @@ import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Operation; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; +import me.topchetoeu.jscript.runtime.values.Member.FieldMember; import me.topchetoeu.jscript.runtime.values.Member.PropertyMember; import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; @@ -68,8 +69,8 @@ public class InstructionRunner { else if (val instanceof FunctionValue func) accessor = func; else throw EngineException.ofType("Getter must be a function or undefined."); - if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, true)); - else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, true)); + if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, instr.get(1))); + else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, instr.get(1))); frame.codePtr++; return null; @@ -79,7 +80,7 @@ public class InstructionRunner { var key = frame.pop(); var obj = frame.pop(); - obj.defineOwnMember(env, key, val); + obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, instr.get(0), true)); frame.codePtr++; return null; diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index b7711eb..e2f8af6 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -1,5 +1,3 @@ -// return; - const target = arguments[0]; const primordials = arguments[1]; @@ -20,28 +18,26 @@ const symbol = primordials.symbol || (() => { }; }); -const number = primordials.number || (() => { - return { - parseInt() { throw new Error("parseInt not supported"); }, - parseFloat() { throw new Error("parseFloat not supported"); }, - isNaN: (val) => val !== val, - NaN: 0 / 0, - Infinity: 1 / 0, - }; -}); +const number = primordials.number || { + parseInt() { throw new Error("parseInt not supported"); }, + parseFloat() { throw new Error("parseFloat not supported"); }, + isNaN: (val) => val !== val, + NaN: 0 / 0, + Infinity: 1 / 0, +}; -const fromCharCode = primordials.string.fromCharCode; -const fromCodePoint = primordials.string.fromCodePoint; -const stringBuild = primordials.string.stringBuild; +const string = primordials.string; -const defineProperty = primordials.object.defineProperty; -const defineField = primordials.object.defineField; -const getOwnMember = primordials.object.getMember; -const getOwnSymbolMember = primordials.object.getOwnSymbolMember; -const getOwnMembers = primordials.object.getOwnMembers; -const getOwnSymbolMembers = primordials.object.getOwnSymbolMembers; -const getPrototype = primordials.object.getPrototype; -const setPrototype = primordials.object.setPrototype; +const object = primordials.object || { + defineProperty() { throw new Error("Define property not polyfillable"); }, + defineField(obj, key, a, b, c, value) { obj[key] = value; }, + getOwnMember() { throw new Error("Get own member not polyfillable"); }, + getOwnSymbolMember() { throw new Error("Get own symbol member not polyfillable"); }, + getOwnMembers() { throw new Error("Get own members not polyfillable"); }, + getOwnSymbolMembers() { throw new Error("Get own symbol members not polyfillable"); }, + getPrototype() { throw new Error("Get prototype not polyfillable"); }, + setPrototype() { throw new Error("Set prototype not polyfillable"); }, +} const invokeType = primordials.function.invokeType; const setConstructable = primordials.function.setConstructable; @@ -97,14 +93,14 @@ class Symbol { setCallable(Symbol, true); setConstructable(Symbol, false); -defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); -defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); -defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); -defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); -defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); -defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); -defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); -defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); +object.defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); +object.defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); +object.defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); +object.defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); +object.defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); +object.defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); +object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); +object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); Symbol(); target.Symbol = Symbol; @@ -160,14 +156,14 @@ class Number { } } -defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); -defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); -defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); -defineField(Number, "POSITIVE_INFINITY", false, false, false, +number.Infinity); -defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity); -defineField(Number, "NaN", false, false, false, number.NaN); -defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); -defineField(Number, "MIN_VALUE", false, false, false, 5e-324); +object.defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); +object.defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); +object.defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); +object.defineField(Number, "POSITIVE_INFINITY", false, false, false, +number.Infinity); +object.defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity); +object.defineField(Number, "NaN", false, false, false, number.NaN); +object.defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); +object.defineField(Number, "MIN_VALUE", false, false, false, 5e-324); setCallable(Number, true); target.Number = Number; @@ -202,20 +198,20 @@ class String { res[arguments.length] = 0; for (let i = 0; i < arguments.length; i++) { - res[i] = fromCharCode(+arguments[i]); + res[i] = string.fromCharCode(+arguments[i]); } - return stringBuild(res); + return string.stringBuild(res); } static fromCodePoint() { const res = []; res[arguments.length] = 0; for (let i = 0; i < arguments.length; i++) { - res[i] = fromCodePoint(+arguments[i]); + res[i] = string.fromCodePoint(+arguments[i]); } - return stringBuild(res); + return string.stringBuild(res); } } @@ -290,8 +286,6 @@ class Object { if ("get" in desc || "set" in desc) { let get = desc.get, set = desc.set; - print(typeof get); - if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); @@ -299,11 +293,11 @@ class Object { throw new TypeError("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); } - if (!defineProperty(obj, key, desc.enumerable, desc.configurable, get, set)) { + if (!object.defineProperty(obj, key, desc.enumerable, desc.configurable, get, set)) { throw new TypeError("Cannot redefine property: " + key); } } - else if (!defineField(obj, key, desc.writable, desc.enumerable, desc.configurable, desc.value)) { + else if (!object.defineField(obj, key, desc.writable, desc.enumerable, desc.configurable, desc.value)) { throw new TypeError("Cannot redefine property: " + key); } @@ -312,7 +306,7 @@ class Object { } setCallable(Object, true); -setPrototype(Object.prototype, null); +object.setPrototype(Object.prototype, null); target.Object = Object; class Function { @@ -322,7 +316,7 @@ class Function { } constructor() { - const parts = ["return function annonymous("]; + const parts = ["(function annonymous("]; for (let i = 0; i < arguments.length - 1; i++) { if (i > 0) parts[parts.length] = ","; @@ -330,9 +324,9 @@ class Function { } parts[parts.length] = "){\n"; parts[parts.length] = String(arguments[arguments.length - 1]); - parts[parts.length] = "\n}"; + parts[parts.length] = "\n})"; - const res = compile(stringBuild(parts))(); + const res = compile(string.stringBuild(parts))(); return res; } @@ -341,27 +335,20 @@ class Function { if (wrap) parts[parts.length] = "return (function() {\n"; if (globals.length > 0) { - parts[parts.length] = "var "; + parts[parts.length] = "let {"; for (let i = 0; i < globals.length; i++) { if (i > 0) parts[parts.length] = ","; parts[parts.length] = globals[i]; } - parts[parts.length] = ";((g=arguments[0])=>{"; - - for (let i = 0; i < globals.length; i++) { - const name = globals[i]; - parts[parts.length] = name + "=g[" + json.stringify(name) + "];"; - } - - parts[parts.length] = "})()\n"; + parts[parts.length] = "} = arguments[0];"; } parts[parts.length] = src; if (wrap) parts[parts.length] = "\n})(arguments[0])"; - const res = compile(stringBuild(parts)); + const res = compile(string.stringBuild(parts)); return res; } } @@ -400,8 +387,8 @@ class Error { } } -defineField(Error.prototype, "name", true, false, true, "Error"); -defineField(Error.prototype, "message", true, false, true, ""); +object.defineField(Error.prototype, "name", true, false, true, "Error"); +object.defineField(Error.prototype, "message", true, false, true, ""); setCallable(Error, true); target.Error = Error; @@ -412,9 +399,9 @@ class SyntaxError { } } -defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); -setPrototype(SyntaxError, Error); -setPrototype(SyntaxError.prototype, Error.prototype); +object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); +object.setPrototype(SyntaxError, Error); +object.setPrototype(SyntaxError.prototype, Error.prototype); setCallable(SyntaxError, true); target.SyntaxError = SyntaxError; @@ -425,9 +412,9 @@ class TypeError { } } -defineField(TypeError.prototype, "name", true, false, true, "TypeError"); -setPrototype(TypeError, Error); -setPrototype(TypeError.prototype, Error.prototype); +object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); +object.setPrototype(TypeError, Error); +object.setPrototype(TypeError.prototype, Error.prototype); setCallable(TypeError, true); target.TypeError = TypeError; @@ -438,9 +425,9 @@ class RangeError { } } -defineField(RangeError.prototype, "name", true, false, true, "RangeError"); -setPrototype(RangeError, Error); -setPrototype(RangeError.prototype, Error.prototype); +object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); +object.setPrototype(RangeError, Error); +object.setPrototype(RangeError.prototype, Error.prototype); setCallable(RangeError, true); target.RangeError = RangeError; -- 2.45.2 From 7fcb9ed19f9e48ea8966a65c264a973075996a06 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Fri, 20 Sep 2024 11:38:50 +0300 Subject: [PATCH 04/41] fix: member field initializers should be iterable --- src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java index 898009a..f265b00 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java @@ -61,7 +61,7 @@ public abstract class ClassNode extends FunctionNode { @Override protected void compilePreBody(CompileResult target) { for (var member : body.protoFields) { target.add(Instruction.loadThis()); - member.compile(target, false, false); + member.compile(target, false, true); } } -- 2.45.2 From ee78bdc1cbc4ddec6e68bfa070ad50a28a215925 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:06:03 +0300 Subject: [PATCH 05/41] feat: implement derived classes --- .../jscript/common/Instruction.java | 34 ++++-- .../common/environment/Environment.java | 2 +- .../jscript/common/parsing/ParseRes.java | 5 + .../jscript/compilation/ClassNode.java | 102 ++++++++++++++++-- .../jscript/compilation/CompileResult.java | 21 +++- .../compilation/FunctionArrowNode.java | 7 +- .../jscript/compilation/FunctionNode.java | 6 +- .../compilation/FunctionStatementNode.java | 2 +- .../compilation/FunctionValueNode.java | 2 +- .../jscript/compilation/JavaScript.java | 10 +- .../compilation/control/ForOfNode.java | 7 +- .../compilation/control/SwitchNode.java | 1 - .../compilation/members/MethodMemberNode.java | 7 +- .../members/PropertyMemberNode.java | 7 +- .../compilation/scope/FunctionScope.java | 15 +++ .../jscript/compilation/scope/Scope.java | 12 +++ .../compilation/scope/VariableList.java | 7 +- .../compilation/values/ClassValueNode.java | 6 +- .../values/operations/CallNode.java | 35 ++++-- .../values/operations/IndexNode.java | 18 +++- .../topchetoeu/jscript/runtime/EventLoop.java | 4 +- .../me/topchetoeu/jscript/runtime/Frame.java | 17 ++- .../jscript/runtime/InstructionRunner.java | 43 ++++++-- .../jscript/runtime/SimpleRepl.java | 6 +- .../jscript/runtime/values/Value.java | 19 ++-- .../values/functions/CodeFunction.java | 11 +- .../values/functions/FunctionValue.java | 1 + .../runtime/values/objects/ObjectValue.java | 4 +- src/main/resources/lib/index.js | 17 +-- 29 files changed, 327 insertions(+), 101 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index f4884ae..60251f9 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -17,11 +17,13 @@ public class Instruction { TRY_END(0x06), CALL(0x10), + @Deprecated CALL_MEMBER(0x11), CALL_NEW(0x12), - JMP_IF(0x13), - JMP_IFN(0x14), - JMP(0x15), + CALL_SUPER(0x13), + JMP_IF(0x18), + JMP_IFN(0x19), + JMP(0x1A), PUSH_UNDEFINED(0x20), PUSH_NULL(0x21), @@ -59,6 +61,7 @@ public class Instruction { KEYS(0x52), TYPEOF(0x53), OPERATION(0x54), + EXTEND(0x55), GLOB_GET(0x60), GLOB_SET(0x61), @@ -282,15 +285,17 @@ public class Instruction { return new Instruction(Type.NOP, params); } - public static Instruction call(int argn, String name) { - return new Instruction(Type.CALL, argn, name); + public static Instruction call(int argn, boolean hasSelf, String name) { + return new Instruction(Type.CALL, argn, hasSelf, name); } - public static Instruction call(int argn) { - return call(argn, ""); + public static Instruction call(int argn, boolean hasSelf) { + return call(argn, hasSelf, ""); } + @Deprecated public static Instruction callMember(int argn, String name) { return new Instruction(Type.CALL_MEMBER, argn, name); } + @Deprecated public static Instruction callMember(int argn) { return new Instruction(Type.CALL_MEMBER, argn, ""); } @@ -300,6 +305,9 @@ public class Instruction { public static Instruction callNew(int argn) { return new Instruction(Type.CALL_NEW, argn, ""); } + public static Instruction callSuper(int argn) { + return new Instruction(Type.CALL_SUPER, argn); + } public static Instruction jmp(int offset) { return new Instruction(Type.JMP, offset); @@ -321,7 +329,6 @@ public class Instruction { return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i); } - public static Instruction pushUndefined() { return new Instruction(Type.PUSH_UNDEFINED); } @@ -386,16 +393,18 @@ public class Instruction { public static Instruction loadRegex(String pattern, String flags) { return new Instruction(Type.LOAD_REGEX, pattern, flags); } - public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, String name, int[] captures) { + // TODO: make this capturing a concern of the compiler + public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, boolean noThis, String name, int[] captures) { if (name == null) name = ""; - var args = new Object[5 + captures.length]; + var args = new Object[6 + captures.length]; args[0] = id; args[1] = name; args[2] = callable; args[3] = constructible; args[4] = captureThis; - for (var i = 0; i < captures.length; i++) args[i + 5] = captures[i]; + args[5] = noThis; + for (var i = 0; i < captures.length; i++) args[i + 6] = captures[i]; return new Instruction(Type.LOAD_FUNC, args); } public static Instruction loadObj() { @@ -460,6 +469,9 @@ public class Instruction { public static Instruction defField(boolean enumerable) { return new Instruction(Type.DEF_FIELD, enumerable); } + public static Instruction extend() { + return new Instruction(Type.EXTEND); + } public static Instruction operation(Operation op) { return new Instruction(Type.OPERATION, op); diff --git a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java index 0a33cec..02302d0 100644 --- a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java +++ b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java @@ -90,7 +90,7 @@ public class Environment { if (has(key)) return get(key); else return defaultVal; } - public T get(Key key, Supplier defaultVal) { + public T getWith(Key key, Supplier defaultVal) { if (has(key)) return get(key); else return defaultVal.get(); } diff --git a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index 3bf2bb8..09b2c65 100644 --- a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java +++ b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -38,6 +38,11 @@ public class ParseRes { return new ParseRes<>(state, errorLocation, error, null, 0); } @SuppressWarnings("unchecked") + public ParseRes chainError(ParseRes other) { + if (!this.isError()) return other.chainError(); + return (ParseRes) this; + } + @SuppressWarnings("unchecked") public ParseRes chainError(Location loc, String error) { if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0); return (ParseRes) this; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java index f265b00..af9585a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java @@ -3,9 +3,13 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; import java.util.LinkedList; import java.util.List; +import java.util.function.Consumer; import me.topchetoeu.jscript.common.Instruction; +import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.Instruction.BreakpointType; +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -14,6 +18,7 @@ import me.topchetoeu.jscript.compilation.members.FieldMemberNode; import me.topchetoeu.jscript.compilation.members.Member; import me.topchetoeu.jscript.compilation.members.MethodMemberNode; import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; +import me.topchetoeu.jscript.compilation.scope.FunctionScope; public abstract class ClassNode extends FunctionNode { public static final class ClassBody { @@ -22,20 +27,29 @@ public abstract class ClassNode extends FunctionNode { public final List protoMembers; public final Parameters constructorParameters; public final CompoundNode constructorBody; + public final Node superExpr; + public final boolean hasConstr; public ClassBody( List staticMembers, List protoFields, List protoMembers, - Parameters constructorParameters, CompoundNode constructorBody + Parameters constructorParameters, CompoundNode constructorBody, + Node superExpr, boolean hasConstr ) { this.staticMembers = staticMembers; this.protoFields = protoFields; this.protoMembers = protoMembers; this.constructorParameters = constructorParameters; this.constructorBody = constructorBody; + this.superExpr = superExpr; + this.hasConstr = hasConstr; } } - // public static final Key + public static final Key CLASS_ROOT = Key.of(); + public static final Key> SUPER = Key.of(); + public static final Key> SUPER_PROTO = Key.of(); + public static final Key> SUPER_CONSTR = Key.of(); + public static final Key> ON_SUPER_CALL = Key.of(); public final ClassBody body; public final String name; @@ -43,7 +57,9 @@ public abstract class ClassNode extends FunctionNode { @Override public String name() { return name; } public void compileStatic(CompileResult target) { - for (var member : body.staticMembers) member.compile(target, true, false); + for (var member : body.staticMembers) { + member.compile(target, true, false); + } } public void compilePrototype(CompileResult target) { if (body.protoMembers.size() > 0) { @@ -58,18 +74,71 @@ public abstract class ClassNode extends FunctionNode { } } - @Override protected void compilePreBody(CompileResult target) { + private void compileFieldInits(CompileResult target) { for (var member : body.protoFields) { target.add(Instruction.loadThis()); member.compile(target, false, true); } } + @Override protected void compilePreBody(CompileResult target) { + if (target.env.hasNotNull(SUPER_PROTO)) { + if (!body.hasConstr) { + throw new SyntaxException(loc(), "Default constructors in derived classes not supported"); + // compileFieldInits(target); + } + } + else compileFieldInits(target); + } + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, false, true, false, name, captures(id, target))); - compileStatic(target); - compilePrototype(target); + if (body.superExpr == null) { + var id = target.addChild(compileBody(target, name, null)); + target.add(_i -> Instruction.loadFunc(id, false, true, false, false, name, captures(id, target))); + compileStatic(target); + compilePrototype(target); + } + else { + var subtarget = target.rootEnvironment(JavaScript.COMPILE_ROOT).subtarget(); + var protoVar = target.scope.defineTemp(); + var constrVar = target.scope.defineTemp(); + + subtarget.env.add(SUPER_PROTO, t -> { + var i = t.scope.get(protoVar, false); + t.add(_i -> i.index().toGet()); + }); + subtarget.env.add(SUPER_CONSTR, t -> { + var i = t.scope.get(constrVar, false); + t.add(_i -> i.index().toGet()); + }); + + var staticTarget = subtarget.subEnvironment(); + staticTarget.env.add(SUPER, subtarget.env.get(SUPER_CONSTR)); + staticTarget.env.add(CLASS_ROOT, staticTarget.env); + + var protoTarget = subtarget.subEnvironment(); + protoTarget.env.add(SUPER, subtarget.env.get(SUPER_PROTO)); + protoTarget.env.add(CLASS_ROOT, protoTarget.env); + + var constrEnv = subtarget.env.child(); + constrEnv.add(SUPER, subtarget.env.get(SUPER_PROTO)); + constrEnv.add(ON_SUPER_CALL, this::compileFieldInits); + constrEnv.add(CLASS_ROOT, constrEnv); + + var id = target.addChild(compileBody(constrEnv, new FunctionScope(subtarget.scope), false, name, null)); + target.add(_i -> Instruction.loadFunc(id, false, true, false, true, name, captures(id, target))); + + body.superExpr.compile(target, true); + + target.add(Instruction.extend()); + target.add(Instruction.dup(1, 0)); + target.add(Instruction.loadMember("prototype")); + target.add(_i -> protoVar.index().toInit()); + target.add(_i -> constrVar.index().toInit()); + + compileStatic(staticTarget); + compilePrototype(protoTarget); + } } public ClassNode(Location loc, Location end, String name, ClassBody body) { @@ -91,7 +160,18 @@ public abstract class ClassNode extends FunctionNode { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); - if (!src.is(i + n, "{")) return ParseRes.failed(); + ParseRes superExpr = ParseRes.failed(); + + if (Parsing.isIdentifier(src, i + n, "extends")) { + n += 7; + + superExpr = JavaScript.parseExpression(src, i + n, 14); + if (!superExpr.isSuccess()) return superExpr.chainError(src.loc(i + n), "Expected an expression after 'extends'"); + n += superExpr.n; + n += Parsing.skipEmpty(src, i + n); + } + + if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected a class body"); n++; n += Parsing.skipEmpty(src, i + n); @@ -105,7 +185,7 @@ public abstract class ClassNode extends FunctionNode { if (src.is(i + n, "}")) { n++; - return ParseRes.res(new ClassBody(statics, fields, members, params, body), n); + return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, false), n); } while (true) { @@ -151,7 +231,7 @@ public abstract class ClassNode extends FunctionNode { else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); } - return ParseRes.res(new ClassBody(statics, fields, members, params, body), n); + return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, hasConstr), n); } // public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 04711d5..c0fb4e1 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.FunctionBody; 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.Key; import me.topchetoeu.jscript.common.mapping.FunctionMap; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; import me.topchetoeu.jscript.common.parsing.Location; @@ -142,7 +143,21 @@ public final class CompileResult { } public CompileResult subtarget() { - return new CompileResult(new Scope(scope), this); + return new CompileResult(env, new Scope(scope), this); + } + + public CompileResult setEnvironment(Environment env) { + return new CompileResult(env, scope, this); + } + /** + * Returns a compile result with a child of the environment that relates to the given key. + * In essence, this is used to create a compile result which is back at the root environment of the compilation + */ + public CompileResult rootEnvironment(Key env) { + return new CompileResult(this.env.get(env).child(), scope, this); + } + public CompileResult subEnvironment() { + return new CompileResult(env.child(), scope, this); } public CompileResult(Environment env, Scope scope, int length, Consumer task) { @@ -154,11 +169,11 @@ public final class CompileResult { this.length = length; this.buildTask = () -> task.accept(this); } - private CompileResult(Scope scope, CompileResult parent) { + private CompileResult(Environment env, Scope scope, CompileResult parent) { this.scope = scope; this.instructions = parent.instructions; this.children = parent.children; this.map = parent.map; - this.env = parent.env; + this.env = env; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java index 83cf64e..d789622 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java @@ -4,6 +4,7 @@ import java.util.Arrays; 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.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -16,7 +17,11 @@ public class FunctionArrowNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, true, null, captures(id, target))); + target.add(_i -> Instruction.loadFunc(id, true, false, true, false, null, captures(id, target))); + } + + @Override protected Environment rootEnv(Environment env) { + return env.getWith(ClassNode.CLASS_ROOT, () -> super.rootEnv(env)); } 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 730cf1f..b4a53b1 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -24,6 +24,10 @@ public abstract class FunctionNode extends Node { protected void compilePreBody(CompileResult target) { } + protected Environment rootEnv(Environment env) { + return env.get(JavaScript.COMPILE_ROOT); + } + public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { var name = this.name() != null ? this.name() : _name; @@ -64,7 +68,7 @@ public abstract class FunctionNode extends Node { }); } public final CompileResult compileBody(CompileResult parent, String name, String selfName) { - return compileBody(parent.env.get(JavaScript.COMPILE_ROOT).child(), new FunctionScope(parent.scope), false, name, selfName); + return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName); } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 6de7691..abaee3e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -17,7 +17,7 @@ public class FunctionStatementNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); + target.add(_i -> Instruction.loadFunc(id, true, true, false, false, name, captures(id, target))); target.add(VariableNode.toInit(target, end, this.name)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index db22d72..cf30f75 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -11,7 +11,7 @@ public class FunctionValueNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, true, false, name, captures(id, target))); + target.add(_i -> Instruction.loadFunc(id, true, true, false, 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 d9ddbc9..e9138e1 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -68,7 +68,7 @@ public final class JavaScript { "finally", "for", "do", "while", "switch", "case", "default", "new", "function", "var", "return", "throw", "typeof", "delete", "break", "continue", "debugger", "implements", "interface", "package", "private", - "protected", "public", "static", "arguments", "class" + "protected", "public", "static", "arguments", "class", "extends" )); public static ParseRes parseParens(Source src, int i) { @@ -130,7 +130,7 @@ public final class JavaScript { return ParseRes.failed(); } - public static ParseRes parseExpression(Source src, int i, int precedence, boolean statement) { + public static ParseRes parseExpression(Source src, int i, int precedence, boolean statement) { var n = Parsing.skipEmpty(src, i); Node prev = null; @@ -174,11 +174,11 @@ public final class JavaScript { else return ParseRes.res(prev, n); } - public static ParseRes parseExpression(Source src, int i, int precedence) { + public static ParseRes parseExpression(Source src, int i, int precedence) { return parseExpression(src, i, precedence, false); } - public static ParseRes parseExpressionStatement(Source src, int i) { + public static ParseRes parseExpressionStatement(Source src, int i) { var res = parseExpression(src, i, 0, true); if (!res.isSuccess()) return res.chainError(); @@ -219,7 +219,7 @@ public final class JavaScript { public static ParseRes parseStatementEnd(Source src, int i) { var n = Parsing.skipEmpty(src, i); - if (i >= src.size()) return ParseRes.res(true, n + 1); + if (i >= src.size()) return ParseRes.res(true, n); for (var j = i; j < i + n; j++) { if (src.is(j, '\n')) return ParseRes.res(true, n); 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 6438c9f..9bd3108 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java @@ -31,13 +31,12 @@ public class ForOfNode extends Node { target.add(Instruction.dup()); target.add(Instruction.loadIntrinsics("it_key")); target.add(Instruction.loadMember()).setLocation(iterable.loc()); - target.add(Instruction.call(0)).setLocation(iterable.loc()); + target.add(Instruction.call(0, true)).setLocation(iterable.loc()); int start = target.size(); - target.add(Instruction.dup()); - target.add(Instruction.dup()); + target.add(Instruction.dup(2, 0)); target.add(Instruction.loadMember("next")).setLocation(iterable.loc()); - target.add(Instruction.call(0)).setLocation(iterable.loc()); + target.add(Instruction.call(0, true)).setLocation(iterable.loc()); target.add(Instruction.dup()); target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); int mid = target.temp(); 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 ee00222..51cb2cc 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -118,7 +118,6 @@ public class SwitchNode extends Node { return ParseRes.res(null, n); } - @SuppressWarnings("unused") public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java index d100366..abc760e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java @@ -2,6 +2,7 @@ package me.topchetoeu.jscript.compilation.members; 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.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -22,12 +23,16 @@ public class MethodMemberNode extends FunctionNode implements Member { else return null; } + @Override protected Environment rootEnv(Environment env) { + return env; + } + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { if (pollute) target.add(Instruction.dup()); key.compile(target, true); var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); + target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target))); } @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java index ff7d748..8290f73 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -4,6 +4,7 @@ import java.util.Arrays; 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.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -29,6 +30,10 @@ public class PropertyMemberNode extends FunctionNode implements Member{ else return null; } + @Override protected Environment rootEnv(Environment env) { + return env; + } + public boolean isGetter() { return argument == null; } public boolean isSetter() { return argument != null; } @@ -37,7 +42,7 @@ public class PropertyMemberNode extends FunctionNode implements Member{ key.compile(target, true); var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, name, captures(id, target))); + target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target))); } 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 c5319bc..efd7cc2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -67,6 +67,21 @@ public class FunctionScope extends Scope { return childVar; } + @Override public Variable get(Variable var, boolean capture) { + if (captures.has(var)) return addCaptured(var, capture); + if (locals.has(var)) return addCaptured(var, capture); + + if (captureParent == null) return null; + + var parentVar = captureParent.get(var, true); + if (parentVar == null) return null; + + var childVar = captures.add(parentVar.clone()); + childToParent.put(childVar, parentVar); + + return childVar; + } + @Override public boolean has(String name, boolean capture) { if (functionVarMap.containsKey(name)) return true; if (specialVarMap.containsKey(name)) return true; 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 62de6c0..8d1a476 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -111,6 +111,18 @@ public class Scope { return null; } + /** + * Gets the index supplier of the given variable, or null if it isn't in this scope chain. + * This will also execute capturing logic. + * + * @param capture If true, the variable is being captured by a function + */ + public Variable get(Variable var, boolean capture) { + if (locals.has(var)) return addCaptured(var, capture); + if (parent != null) return parent.get(var, capture); + + return null; + } /** * Checks if the given variable name is accessible * 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 3e71689..5d8dcb5 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -64,7 +64,7 @@ public final class VariableList { public boolean frozen() { if (frozenList != null) { assert frozenList != null; - assert varMap == null; + assert varMap != null; assert first == null; assert last == null; @@ -139,6 +139,10 @@ public final class VariableList { return node.var; } + public boolean has(Variable var) { + return varMap.containsKey(var); + } + public Supplier indexer(Variable var) { return varMap.get(var); } @@ -162,7 +166,6 @@ public final class VariableList { } first = last = null; - varMap = null; } public Iterable all() { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java index cf4434e..e0240f4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java @@ -5,6 +5,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.ClassNode; +import me.topchetoeu.jscript.compilation.JavaScript; public class ClassValueNode extends ClassNode { public ClassValueNode(Location loc, Location end, String name, ClassBody body) { @@ -19,10 +20,13 @@ public class ClassValueNode extends ClassNode { n += 5; var name = Parsing.parseIdentifier(src, i + n); + if (name.isSuccess() && !JavaScript.checkVarName(name.result)) { + name = ParseRes.error(src.loc(i + n), "Unexpected keyword '" + name.result + "'"); + } n += name.n; var body = parseBody(src, i + n); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body"); + if (!body.isSuccess()) return body.chainError(name).chainError(src.loc(i + n), "Expected a class body"); n += body.n; return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index d1414bb..b225827 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -10,12 +10,14 @@ 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.ClassNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.SuperNode; import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode; @@ -88,18 +90,37 @@ public class CallNode extends Node { } @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - if (!isNew && func instanceof IndexNode) { - var obj = ((IndexNode)func).object; - var index = ((IndexNode)func).index; + var superInstr = target.env.get(ClassNode.SUPER); + + if (!isNew && func instanceof SuperNode && superInstr != null && target.env.hasNotNull(ClassNode.ON_SUPER_CALL)) { + target.env.get(ClassNode.SUPER_CONSTR).accept(target); + for (var arg : args) arg.compile(target, true); + target.add(Instruction.callSuper(args.length)); + target.env.get(ClassNode.ON_SUPER_CALL).accept(target); + } + else if (!isNew && func instanceof IndexNode indexn) { + var obj = indexn.object; + var index = indexn.index; String name = ""; - obj.compile(target, true); - index.compile(target, true); + if (superInstr != null && obj instanceof SuperNode) { + new ThisNode(null).compile(target, true); + target.env.get(ClassNode.SUPER).accept(target); + IndexNode.indexLoad(target, index, true); + } + else { + obj.compile(target, true); + target.add(Instruction.dup()); + IndexNode.indexLoad(target, index, true); + } + for (var arg : args) arg.compile(target, true); if (ATTACH_NAME) name = generateName(obj, index); - target.add(Instruction.callMember(args.length, name)).setLocationAndDebug(loc(), type); + target.add(Instruction.call(args.length, true, name)); + + target.setLocationAndDebug(loc(), type); } else { String name = ""; @@ -110,7 +131,7 @@ public class CallNode extends Node { if (ATTACH_NAME) name = generateName(func, null); if (isNew) target.add(Instruction.callNew(args.length, name)).setLocationAndDebug(loc(), type); - else target.add(Instruction.call(args.length, name)).setLocationAndDebug(loc(), type); + else target.add(Instruction.call(args.length, false, name)).setLocationAndDebug(loc(), type); } if (!pollute) target.add(Instruction.discard()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index 826b8cd..752e9a9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -6,10 +6,12 @@ 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.ClassNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; +import me.topchetoeu.jscript.compilation.values.SuperNode; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; @@ -18,12 +20,11 @@ public class IndexNode extends Node implements ChangeTarget { public final Node index; @Override public void beforeAssign(CompileResult target) { - object.compile(target, true); - + compileObj(target, object); indexStorePushKey(target, index); } @Override public void beforeChange(CompileResult target) { - object.compile(target, true); + compileObj(target, object); if (index instanceof NumberNode num && (int)num.value == num.value) { target.add(Instruction.dup()); @@ -43,7 +44,7 @@ public class IndexNode extends Node implements ChangeTarget { } @Override public void assign(CompileResult target, boolean pollute) { - object.compile(target, true); + compileObj(target, object); target.add(Instruction.dup(1, 1)); indexStorePushKey(target, index); indexStore(target, index, pollute); @@ -54,7 +55,7 @@ public class IndexNode extends Node implements ChangeTarget { } public void compile(CompileResult target, boolean dupObj, boolean pollute) { - object.compile(target, true); + compileObj(target, object); if (dupObj) target.add(Instruction.dup()); if (index instanceof NumberNode num && (int)num.value == num.value) { @@ -82,6 +83,13 @@ public class IndexNode extends Node implements ChangeTarget { this.index = index; } + private static void compileObj(CompileResult target, Node obj) { + if (obj instanceof SuperNode && target.env.hasNotNull(ClassNode.SUPER)) { + target.env.get(ClassNode.SUPER).accept(target); + } + else obj.compile(target, true); + } + public static ParseRes parseIndex(Source src, int i, Node prev, int precedence) { if (precedence > 18) return ParseRes.failed(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java b/src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java index 6a929a0..6126684 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/EventLoop.java @@ -28,9 +28,9 @@ public interface EventLoop { } public default Future pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { - return pushMsg(() -> func.invoke(env, thisArg, args), micro); + return pushMsg(() -> func.apply(env, thisArg, args), micro); } public default Future pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { - return pushMsg(() -> Compiler.compileFunc(env, filename, raw).invoke(env, thisArg, args), micro); + return pushMsg(() -> Compiler.compileFunc(env, filename, raw).apply(env, thisArg, args), micro); } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index 17e5a2c..636a881 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -15,6 +15,11 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class Frame { public static final Key KEY = Key.of(); + public static final EngineException STACK_OVERFLOW; + static { + STACK_OVERFLOW = EngineException.ofRange("Stack overflow!"); + STACK_OVERFLOW.value.freeze(); + } public static enum TryState { TRY, @@ -193,6 +198,7 @@ public final class Frame { } } } + catch (StackOverflowError e) { throw STACK_OVERFLOW; } catch (EngineException e) { error = e; } catch (RuntimeException e) { System.out.println(dbg.getMapOrEmpty(function).toLocation(codePtr, true)); @@ -268,6 +274,13 @@ public final class Frame { } } + if (returnValue != null) { + if (self == null) error = EngineException.ofError("Super constructor must be called before returning"); + else { + dbg.onInstruction(env, this, instr, returnValue, null, false); + return returnValue; + } + } if (error != null) { var caught = false; @@ -280,10 +293,6 @@ public final class Frame { dbg.onInstruction(env, this, instr, null, error, caught); throw error; } - if (returnValue != null) { - dbg.onInstruction(env, this, instr, returnValue, null, false); - return returnValue; - } return null; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 8bdc6dd..10d1b15 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -32,18 +32,22 @@ public class InstructionRunner { private static Value execCall(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); var func = frame.pop(); + var self = (boolean)instr.get(1) ? frame.pop() : Value.UNDEFINED; - frame.push(func.call(env, false, instr.get(1), Value.UNDEFINED, callArgs)); + frame.push(func.apply(env, instr.get(2), self, callArgs)); frame.codePtr++; return null; } - private static Value execCallMember(Environment env, Instruction instr, Frame frame) { + private static Value execCallSuper(Environment env, Instruction instr, Frame frame) { var callArgs = frame.take(instr.get(0)); - var index = frame.pop(); - var obj = frame.pop(); + var superFunc = frame.pop(); - frame.push(obj.getMember(env, index).call(env, false, instr.get(1), obj, callArgs)); + var self = new ObjectValue(); + if (frame.function.prototype instanceof ObjectValue objProto) self.setPrototype(env, objProto); + + frame.self = superFunc.construct(env, "super", self, callArgs); + frame.push(frame.self); frame.codePtr++; return null; @@ -185,11 +189,12 @@ public class InstructionRunner { boolean callable = instr.get(2); boolean constructible = instr.get(3); boolean captureThis = instr.get(4); - - var captures = new Value[instr.params.length - 5][]; + boolean noThis = instr.get(5); - for (var i = 5; i < instr.params.length; i++) { - captures[i - 5] = frame.captureVar(instr.get(i)); + var captures = new Value[instr.params.length - 6][]; + + for (var i = 6; i < instr.params.length; i++) { + captures[i - 6] = frame.captureVar(instr.get(i)); } var func = new CodeFunction(env, name, frame.function.body.children[id], captures); @@ -199,6 +204,8 @@ public class InstructionRunner { func.self = frame.self; func.argsVal = frame.argsVal; } + if (noThis) func.mustCallSuper = true; + frame.push(func); frame.codePtr++; @@ -487,6 +494,20 @@ public class InstructionRunner { frame.codePtr++; return null; } + private static Value execExtend(Environment env, Instruction instr, Frame frame) { + var superVal = frame.peek(0); + var derivedVal = frame.peek(1); + + if (!(superVal instanceof FunctionValue superFunc)) throw EngineException.ofType("Illegal EXTENDS instruction"); + if (!(superFunc.prototype instanceof ObjectValue superProto)) throw EngineException.ofType("Illegal EXTENDS instruction"); + if (!(derivedVal instanceof FunctionValue derivedFunc)) throw EngineException.ofType("Illegal EXTENDS instruction"); + + derivedFunc.setPrototype(env, superFunc); + derivedFunc.prototype.setPrototype(env, superProto); + + frame.codePtr++; + return null; + } private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) { if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal); @@ -510,6 +531,7 @@ public class InstructionRunner { return null; } private static Value execLoadThis(Environment env, Instruction instr, Frame frame) { + if (frame.self == null) throw EngineException.ofError("Super constructor must be called before 'this' is accessed"); frame.push(frame.self); frame.codePtr++; return null; @@ -547,7 +569,7 @@ public class InstructionRunner { case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); case CALL: return execCall(env, instr, frame); case CALL_NEW: return execCallNew(env, instr, frame); - case CALL_MEMBER: return execCallMember(env, instr, frame); + case CALL_SUPER: return execCallSuper(env, instr, frame); case TRY_START: return execTryStart(env, instr, frame); case TRY_END: return execTryEnd(env, instr, frame); @@ -595,6 +617,7 @@ public class InstructionRunner { case GLOB_DEF: return exexGlobDef(env, instr, frame); case GLOB_GET: return exexGlobGet(env, instr, frame); case GLOB_SET: return exexGlobSet(env, instr, frame); + case EXTEND: return execExtend(env, instr, frame); case VAR_INIT: return execVarInit(env, instr, frame); case VAR_FREE: return execVarFree(env, instr, frame); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 5518ec7..b6047dc 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -231,7 +231,7 @@ public class SimpleRepl { var funcArgs = (ArrayValue)args.get(2); var name = args.get(3).toString(env); - return func.invoke(env, name, self, funcArgs.toArray()); + return func.apply(env, name, self, funcArgs.toArray()); })); res.defineOwnMember(env, "construct", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); @@ -263,7 +263,7 @@ public class SimpleRepl { var self = args.get(1); var funcArgs = (ArrayValue)args.get(2); - return func.invoke(env, self, funcArgs.toArray()); + return func.apply(env, self, funcArgs.toArray()); })); return res; @@ -362,7 +362,7 @@ public class SimpleRepl { glob.defineOwnMember(null, "measure", new NativeFunction("measure", args -> { var start = System.nanoTime(); - ((FunctionValue)args.get(0)).invoke(args.env, Value.UNDEFINED); + ((FunctionValue)args.get(0)).apply(args.env, Value.UNDEFINED); System.out.println(String.format("Finished in %sns", System.nanoTime() - start)); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index 2979392..817ef2d 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -89,7 +89,7 @@ public abstract class Value { else throw EngineException.ofType(name + " is not a function"); } - public final Value invoke(Environment env, String name, Value self, Value ...args) { + public final Value apply(Environment env, String name, Value self, Value ...args) { return call(env, false, name, self, args); } public final Value construct(Environment env, String name, Value ...args) { @@ -97,16 +97,19 @@ public abstract class Value { var proto = getMember(env, StringValue.of("prototype")); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); - else res.setPrototype(env, null); var ret = this.call(env, true, name, res, args); if (!ret.isPrimitive()) return ret; return res; } + public final Value construct(Environment env, String name, Value self, Value ...args) { + var ret = this.call(env, true, name, self, args); + return ret.isPrimitive() ? self : ret; + } - public final Value invoke(Environment env, Value self, Value ...args) { - return invoke(env, "", self, args); + public final Value apply(Environment env, Value self, Value ...args) { + return apply(env, "", self, args); } public final Value construct(Environment env, Value ...args) { return construct(env, "", args); @@ -118,7 +121,7 @@ public abstract class Value { public abstract boolean toBoolean(); public final boolean isInstanceOf(Environment env, Value proto) { - for (var val = getPrototype(env); val != null; val = getPrototype(env)) { + for (var val = getPrototype(env); val != null; val = val.getPrototype(env)) { if (val.equals(proto)) return true; } @@ -390,7 +393,7 @@ public abstract class Value { private void loadNext() { if (supplier == null) value = null; else if (consumed) { - var curr = supplier.invoke(env, Value.UNDEFINED); + var curr = supplier.apply(env, Value.UNDEFINED); if (curr == null) { supplier = null; value = null; } if (curr.getMember(env, StringValue.of("done")).toBoolean()) { supplier = null; value = null; } @@ -418,12 +421,12 @@ public abstract class Value { public void callWith(Environment env, Iterable it) { for (var el : it) { - this.invoke(env, Value.UNDEFINED, el); + this.apply(env, Value.UNDEFINED, el); } } public void callWithAsync(Environment env, Iterable it, boolean async) { for (var el : it) { - env.get(EventLoop.KEY).pushMsg(() -> this.invoke(env, Value.UNDEFINED, el), true); + env.get(EventLoop.KEY).pushMsg(() -> this.apply(env, Value.UNDEFINED, el), true); } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 7d900fa..5bf781e 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -8,11 +8,13 @@ import me.topchetoeu.jscript.runtime.values.Value; public final class CodeFunction extends FunctionValue { public final FunctionBody body; public final Value[][] captures; - public Value self; - public Value argsVal; public Environment env; + public Value self; + public Value argsVal; + private Value onCall(Frame frame) { + if (mustCallSuper) frame.self = null; frame.onPush(); try { @@ -31,7 +33,10 @@ public final class CodeFunction extends FunctionValue { if (argsVal != null) frame.fakeArgs = argsVal; if (self != null) frame.self = self; - return onCall(frame); + var res = onCall(frame); + + if (isNew) return frame.self; + else return res; } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 6bfbb8d..b44c76b 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -23,6 +23,7 @@ public abstract class FunctionValue extends ObjectValue { public boolean enableCall = true; public boolean enableNew = true; + public boolean mustCallSuper = false; private final FieldMember nameField = new FieldMember(this, true, false, false) { @Override public Value get(Environment env, Value self) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java index 616b775..12e67b0 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/objects/ObjectValue.java @@ -46,13 +46,13 @@ public class ObjectValue extends Value { var valueOf = getMember(env, "valueOf"); if (valueOf instanceof FunctionValue) { - var res = valueOf.invoke(env, this); + var res = valueOf.apply(env, this); if (res.isPrimitive()) return res; } var toString = getMember(env, "toString"); if (toString instanceof FunctionValue) { - var res = toString.invoke(env, this); + var res = toString.apply(env, this); if (res.isPrimitive()) return res; } } diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index e2f8af6..1d02395 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -241,7 +241,6 @@ target.Boolean = Boolean; class Object { toString() { - print("2"); if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; else if (typeof this === "number" || this instanceof Number) return "[object Number]"; else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; @@ -251,13 +250,11 @@ class Object { else return "[object Object]"; } valueOf() { - print("1"); return this; } constructor(value) { if (typeof value === 'object' && value !== null) return value; - if (typeof value === 'string') return new String(value); if (typeof value === 'number') return new Number(value); if (typeof value === 'boolean') return new Boolean(value); @@ -268,19 +265,15 @@ class Object { return res; } - const target = this; - // TODO: use new.target.prototype as proto - if (target == null || typeof target !== 'object') target = {}; + return {}; + // // TODO: use new.target.prototype as proto + // if (target == null || typeof target !== 'object') target = {}; - this[valueKey] = Object(value); + // return target; } static defineProperty(obj, key, desc) { - if (typeof obj !== "object" || obj === null) { - print(obj); - print(typeof obj); - throw new TypeError("Object.defineProperty called on non-object"); - } + if (typeof obj !== "object" || obj === null) throw new TypeError("Object.defineProperty called on non-object"); if (typeof desc !== "object" || desc === null) throw new TypeError("Property description must be an object: " + desc); if ("get" in desc || "set" in desc) { -- 2.45.2 From 9845a39e84725e4fd964745b6a5f0c17394f466a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:42:34 +0300 Subject: [PATCH 06/41] fix: operations polluting stack --- .../jscript/compilation/values/operations/OperationNode.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java index 5189330..05b8fa9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java @@ -110,8 +110,8 @@ public class OperationNode extends Node { arg.compile(target, true); } - if (pollute) target.add(Instruction.operation(operation)); - else target.add(Instruction.discard()); + target.add(Instruction.operation(operation)); + if (!pollute) target.add(Instruction.discard()); } public OperationNode(Location loc, Operation operation, Node ...args) { -- 2.45.2 From fee74dcba4c5b94d59cca890d2fa1859a71666f6 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:42:51 +0300 Subject: [PATCH 07/41] fix: infinite loop in class parser --- .../topchetoeu/jscript/compilation/members/FieldMemberNode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java index 4402693..53edbf3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -75,7 +75,7 @@ public class FieldMemberNode implements Member { n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, "=")) { - var end = JavaScript.parseStatement(src, i + n); + var end = JavaScript.parseStatementEnd(src, i + n); if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer"); n += end.n; -- 2.45.2 From 797452c28f5f00df2192991d99cd3647c64eb737 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:43:15 +0300 Subject: [PATCH 08/41] fix: tmp variables captured incorrectly --- .../topchetoeu/jscript/compilation/scope/FunctionScope.java | 4 ++++ .../java/me/topchetoeu/jscript/compilation/scope/Scope.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) 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 efd7cc2..81850c3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -14,6 +14,7 @@ public class FunctionScope extends Scope { private final HashSet blacklistNames = new HashSet<>(); private final HashMap childToParent = new HashMap<>(); + private final HashMap parentToChild = new HashMap<>(); private final Scope captureParent; @@ -63,11 +64,13 @@ public class FunctionScope extends Scope { var childVar = captures.add(parentVar.clone()); capturesMap.put(childVar.name, childVar); childToParent.put(childVar, parentVar); + parentToChild.put(parentVar, childVar); return childVar; } @Override public Variable get(Variable var, boolean capture) { + if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture); if (captures.has(var)) return addCaptured(var, capture); if (locals.has(var)) return addCaptured(var, capture); @@ -78,6 +81,7 @@ public class FunctionScope extends Scope { var childVar = captures.add(parentVar.clone()); childToParent.put(childVar, parentVar); + parentToChild.put(parentVar, childVar); return childVar; } 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 8d1a476..92ffdd4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java @@ -29,7 +29,7 @@ public class Scope { protected void transferCaptured(Variable var) { if (!singleEntry) { - this.capturables.add(var); + if (!this.capturables.has(var)) this.capturables.add(var); } else if (parent != null) { parent.transferCaptured(var); -- 2.45.2 From 98e5299f9c633695a741b1b3f82e063f15c1efca Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:43:32 +0300 Subject: [PATCH 09/41] fix: derived classes use the scope API incorrectly --- .../java/me/topchetoeu/jscript/compilation/ClassNode.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java index af9585a..3efad3b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java @@ -99,7 +99,9 @@ public abstract class ClassNode extends FunctionNode { compilePrototype(target); } else { - var subtarget = target.rootEnvironment(JavaScript.COMPILE_ROOT).subtarget(); + var subtarget = target.subtarget().rootEnvironment(JavaScript.COMPILE_ROOT); + subtarget.scope.singleEntry = true; + subtarget.beginScope(); var protoVar = target.scope.defineTemp(); var constrVar = target.scope.defineTemp(); @@ -138,6 +140,7 @@ public abstract class ClassNode extends FunctionNode { compileStatic(staticTarget); compilePrototype(protoTarget); + subtarget.endScope(); } } @@ -228,7 +231,7 @@ public abstract class ClassNode extends FunctionNode { n++; break; } - else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); + // else return ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); } return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, hasConstr), n); -- 2.45.2 From 78af69ec8000199f949e42d5d557d7d875916a3e Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:44:08 +0300 Subject: [PATCH 10/41] fix: parseStatementEnd behaving incorrectly when EOF --- src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index e9138e1..5110a11 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -219,7 +219,7 @@ public final class JavaScript { public static ParseRes parseStatementEnd(Source src, int i) { var n = Parsing.skipEmpty(src, i); - if (i >= src.size()) return ParseRes.res(true, n); + if (i + n >= src.size()) return ParseRes.res(true, n); for (var j = i; j < i + n; j++) { if (src.is(j, '\n')) return ParseRes.res(true, n); -- 2.45.2 From bd548c813a91c5c5e583772abff6cde8ec454927 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:45:38 +0300 Subject: [PATCH 11/41] fix: null out thisArg only when constructing --- .../jscript/runtime/values/functions/CodeFunction.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 5bf781e..e72a38a 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -14,7 +14,6 @@ public final class CodeFunction extends FunctionValue { public Value argsVal; private Value onCall(Frame frame) { - if (mustCallSuper) frame.self = null; frame.onPush(); try { @@ -32,6 +31,7 @@ public final class CodeFunction extends FunctionValue { var frame = new Frame(env, isNew, thisArg, args, this); if (argsVal != null) frame.fakeArgs = argsVal; if (self != null) frame.self = self; + if (mustCallSuper && isNew) frame.self = null; var res = onCall(frame); -- 2.45.2 From 0064c74ac8b5a0f90c524a5160d4bc1a24b563a7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:46:02 +0300 Subject: [PATCH 12/41] fix: don't allow execution of CALL_SUPER twice or in non-construct call --- .../java/me/topchetoeu/jscript/runtime/InstructionRunner.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index 10d1b15..db4ec83 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -40,6 +40,9 @@ public class InstructionRunner { return null; } private static Value execCallSuper(Environment env, Instruction instr, Frame frame) { + if (!frame.isNew) throw EngineException.ofError("Super constructor may be called only when constructing"); + if (frame.self != null) throw EngineException.ofError("Super constructor may be called once"); + var callArgs = frame.take(instr.get(0)); var superFunc = frame.pop(); -- 2.45.2 From d821a3a89be5bfe173acf9c19fbc652d284251a2 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 18:46:22 +0300 Subject: [PATCH 13/41] refactor: utilize inheritence in index.js --- src/main/resources/lib/index.js | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index 1d02395..023a569 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -385,42 +385,36 @@ object.defineField(Error.prototype, "message", true, false, true, ""); setCallable(Error, true); target.Error = Error; -class SyntaxError { - constructor (msg = "") { +class SyntaxError extends Error { + constructor (msg) { if (invokeType(arguments) === "call") return new SyntaxError(msg); - this.message = msg + ""; + super(msg); } } object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); -object.setPrototype(SyntaxError, Error); -object.setPrototype(SyntaxError.prototype, Error.prototype); setCallable(SyntaxError, true); target.SyntaxError = SyntaxError; -class TypeError { - constructor (msg = "") { +class TypeError extends Error { + constructor (msg) { if (invokeType(arguments) === "call") return new TypeError(msg); - this.message = msg + ""; + super(msg); } } object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); -object.setPrototype(TypeError, Error); -object.setPrototype(TypeError.prototype, Error.prototype); setCallable(TypeError, true); target.TypeError = TypeError; -class RangeError { - constructor (msg = "") { +class RangeError extends Error { + constructor (msg) { if (invokeType(arguments) === "call") return new RangeError(msg); - this.message = msg + ""; + super(msg); } } object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); -object.setPrototype(RangeError, Error); -object.setPrototype(RangeError.prototype, Error.prototype); setCallable(RangeError, true); target.RangeError = RangeError; -- 2.45.2 From 2fe5ce607a73ecd989066ed2fd2e238f547e6751 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 21 Sep 2024 19:01:05 +0300 Subject: [PATCH 14/41] fix: multiply acting as subtract --- src/main/java/me/topchetoeu/jscript/runtime/values/Value.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index 817ef2d..e70d05d 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -547,8 +547,8 @@ public abstract class Value { var na = a.toNumber(env); var nb = b.toNumber(env); - if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() - nb.getInt()); - else return NumberValue.of(na.getDouble() - nb.getDouble()); + if (na.isInt() && nb.isInt()) return NumberValue.of(na.getInt() * nb.getInt()); + else return NumberValue.of(na.getDouble() * nb.getDouble()); } public static final NumberValue divide(Environment env, Value a, Value b) { var na = a.toNumber(env); -- 2.45.2 From 0ebf189c95c2422759e6c16b7f366ade87b0d954 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:01:00 +0200 Subject: [PATCH 15/41] fix: remove multi-key bullcrap --- .../common/environment/Environment.java | 90 ------------------- .../jscript/common/environment/Key.java | 4 +- .../jscript/common/environment/MultiKey.java | 7 -- 3 files changed, 2 insertions(+), 99 deletions(-) delete mode 100644 src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java diff --git a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java index 02302d0..74be652 100644 --- a/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java +++ b/src/main/java/me/topchetoeu/jscript/common/environment/Environment.java @@ -3,7 +3,6 @@ package me.topchetoeu.jscript.common.environment; import java.util.HashMap; import java.util.HashSet; import java.util.Map; -import java.util.Random; import java.util.Set; import java.util.function.Supplier; @@ -12,71 +11,13 @@ public class Environment { private final Map, Object> map = new HashMap<>(); private final Set> hidden = new HashSet<>(); - private final Map, Set> multi = new HashMap<>(); - private final Map, Set> multiHidden = new HashMap<>(); - - @SuppressWarnings("unchecked") - private Set getAll(MultiKey key, boolean forceClone) { - Set parent = null, child = null; - boolean cloned = false; - - if (this.parent != null && !hidden.contains(key)) { - parent = this.parent.getAll(key, false); - if (parent.size() == 0) parent = null; - else if (multiHidden.containsKey(key)) { - parent = new HashSet<>(parent); - parent.removeAll(multiHidden.get(key)); - cloned = true; - } - } - if (multi.containsKey(key)) { - child = (Set)multi.get(key); - if (child.size() == 0) child = null; - } - - if (!forceClone) { - if (parent == null && child == null) return new HashSet<>(); - if (parent == null && child != null) return child; - if (parent != null && child == null) return parent; - } - - if (!cloned) parent = new HashSet<>(); - parent.addAll(child); - return parent; - } - private T getMulti(MultiKey key) { - return key.of(getAll(key, false)); - } - private boolean hasMulti(MultiKey key) { - return getAll(key, false).size() > 0; - } - - @SuppressWarnings("all") - private Environment addMulti(MultiKey key, T value) { - if (!multi.containsKey(key)) { - if (hidden.contains(key)) { - multiHidden.put((MultiKey)key, (Set)parent.getAll(key, true)); - hidden.remove(key); - } - - multi.put((MultiKey)key, new HashSet<>()); - } - - multi.get(key).add(value); - return this; - } - @SuppressWarnings("unchecked") public T get(Key key) { - if (key instanceof MultiKey) return getMulti((MultiKey)key); - if (map.containsKey(key)) return (T)map.get(key); else if (!hidden.contains(key) && parent != null) return parent.get(key); else return null; } public boolean has(Key key) { - if (key instanceof MultiKey) return hasMulti((MultiKey)key); - if (map.containsKey(key)) return true; else if (!hidden.contains(key) && parent != null) return parent.has(key); else return false; @@ -97,8 +38,6 @@ public class Environment { @SuppressWarnings("unchecked") public Environment add(Key key, T val) { - if (key instanceof MultiKey) return add(key, val); - map.put((Key)key, val); hidden.remove(key); return this; @@ -108,14 +47,6 @@ public class Environment { } @SuppressWarnings("all") public Environment addAll(Map, ?> map, boolean iterableAsMulti) { - for (var pair : map.entrySet()) { - if (iterableAsMulti && pair.getKey() instanceof MultiKey && pair.getValue() instanceof Iterable) { - for (var val : (Iterable)pair.getValue()) { - addMulti((MultiKey)pair.getKey(), val); - } - } - else add((Key)pair.getKey(), pair.getValue()); - } map.putAll((Map)map); hidden.removeAll(map.keySet()); return this; @@ -127,26 +58,9 @@ public class Environment { @SuppressWarnings("unchecked") public Environment remove(Key key) { map.remove(key); - multi.remove(key); - multiHidden.remove(key); hidden.add((Key)key); return this; } - @SuppressWarnings("all") - public Environment remove(MultiKey key, T val) { - if (multi.containsKey(key)) { - multi.get(key).remove(val); - multiHidden.get(key).add(val); - - if (multi.get(key).size() == 0) { - multi.remove(key); - multiHidden.remove(key); - hidden.add((Key)key); - } - } - - return this; - } public T init(Key key, T val) { if (!has(key)) this.add(key, val); @@ -180,8 +94,4 @@ public class Environment { public static Environment empty() { return new Environment(); } - - public static int nextId() { - return new Random().nextInt(); - } } diff --git a/src/main/java/me/topchetoeu/jscript/common/environment/Key.java b/src/main/java/me/topchetoeu/jscript/common/environment/Key.java index 1bb96b3..60470dd 100644 --- a/src/main/java/me/topchetoeu/jscript/common/environment/Key.java +++ b/src/main/java/me/topchetoeu/jscript/common/environment/Key.java @@ -1,7 +1,7 @@ package me.topchetoeu.jscript.common.environment; -public interface Key { +public final class Key { public static Key of() { - return new Key<>() { }; + return new Key<>(); } } diff --git a/src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java b/src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java deleted file mode 100644 index 79e8404..0000000 --- a/src/main/java/me/topchetoeu/jscript/common/environment/MultiKey.java +++ /dev/null @@ -1,7 +0,0 @@ -package me.topchetoeu.jscript.common.environment; - -import java.util.Set; - -public interface MultiKey extends Key { - public T of(Set values); -} -- 2.45.2 From 45308e6d65527f3d815993c4731f8944c5adf29b Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:04:03 +0200 Subject: [PATCH 16/41] refactor: remove periods from ends of error msgs --- .../java/me/topchetoeu/jscript/common/json/JSON.java | 2 +- .../topchetoeu/jscript/common/json/JSONElement.java | 12 ++++++------ .../me/topchetoeu/jscript/common/json/JSONMap.java | 10 +++++----- .../topchetoeu/jscript/common/parsing/ParseRes.java | 2 +- .../topchetoeu/jscript/common/parsing/Parsing.java | 4 ++-- .../java/me/topchetoeu/jscript/runtime/Compiler.java | 2 +- 6 files changed, 16 insertions(+), 16 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/json/JSON.java b/src/main/java/me/topchetoeu/jscript/common/json/JSON.java index 56c34c9..e5dcce0 100644 --- a/src/main/java/me/topchetoeu/jscript/common/json/JSON.java +++ b/src/main/java/me/topchetoeu/jscript/common/json/JSON.java @@ -102,7 +102,7 @@ public class JSON { if (filename == null) filename = new Filename("jscript", "json"); var res = parseValue(new Source(null, filename, raw), 0); - if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given."); + if (res.isFailed()) throw new SyntaxException(null, "Invalid JSON given"); else if (res.isError()) throw new SyntaxException(null, res.error); else return JSONElement.of(res.result); } diff --git a/src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java b/src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java index a8d8490..258497e 100644 --- a/src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java +++ b/src/main/java/me/topchetoeu/jscript/common/json/JSONElement.java @@ -35,7 +35,7 @@ public class JSONElement { else if (val instanceof Boolean) return bool((Boolean)val); else if (val instanceof Number) return number(((Number)val).doubleValue()); else if (val == null) return NULL; - else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap."); + else throw new IllegalArgumentException("val must be: String, Boolean, Number, JSONList or JSONMap"); } public final Type type; @@ -49,23 +49,23 @@ public class JSONElement { public boolean isNull() { return type == Type.NULL; } public JSONMap map() { - if (!isMap()) throw new IllegalStateException("Element is not a map."); + if (!isMap()) throw new IllegalStateException("Element is not a map"); return (JSONMap)value; } public JSONList list() { - if (!isList()) throw new IllegalStateException("Element is not a map."); + if (!isList()) throw new IllegalStateException("Element is not a map"); return (JSONList)value; } public String string() { - if (!isString()) throw new IllegalStateException("Element is not a string."); + if (!isString()) throw new IllegalStateException("Element is not a string"); return (String)value; } public double number() { - if (!isNumber()) throw new IllegalStateException("Element is not a number."); + if (!isNumber()) throw new IllegalStateException("Element is not a number"); return (double)value; } public boolean bool() { - if (!isBoolean()) throw new IllegalStateException("Element is not a boolean."); + if (!isBoolean()) throw new IllegalStateException("Element is not a boolean"); return (boolean)value; } diff --git a/src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java b/src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java index 2e2f1f9..2906910 100644 --- a/src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java +++ b/src/main/java/me/topchetoeu/jscript/common/json/JSONMap.java @@ -51,7 +51,7 @@ public class JSONMap implements Map { public JSONMap map(String path) { var el = get(path); - if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path)); + if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path)); return el.map(); } public JSONMap map(String path, JSONMap defaultVal) { @@ -63,7 +63,7 @@ public class JSONMap implements Map { public JSONList list(String path) { var el = get(path); - if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path)); + if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path)); return el.list(); } public JSONList list(String path, JSONList defaultVal) { @@ -75,7 +75,7 @@ public class JSONMap implements Map { public String string(String path) { var el = get(path); - if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path)); + if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path)); return el.string(); } public String string(String path, String defaultVal) { @@ -87,7 +87,7 @@ public class JSONMap implements Map { public double number(String path) { var el = get(path); - if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path)); + if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path)); return el.number(); } public double number(String path, double defaultVal) { @@ -99,7 +99,7 @@ public class JSONMap implements Map { public boolean bool(String path) { var el = get(path); - if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist.", path)); + if (el == null) throw new RuntimeException(String.format("'%s' doesn't exist", path)); return el.bool(); } public boolean bool(String path, boolean defaultVal) { diff --git a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java index 09b2c65..11502e8 100644 --- a/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java +++ b/src/main/java/me/topchetoeu/jscript/common/parsing/ParseRes.java @@ -34,7 +34,7 @@ public class ParseRes { return new ParseRes<>(state, null, null, result, this.n + n); } public ParseRes chainError() { - if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed."); + if (isSuccess()) throw new RuntimeException("Can't transform a ParseRes that hasn't failed"); return new ParseRes<>(state, errorLocation, error, null, 0); } @SuppressWarnings("unchecked") diff --git a/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java index fb5acc6..96886d8 100644 --- a/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java +++ b/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -86,10 +86,10 @@ public class Parsing { var newC = 0; for (var j = 0; j < 2; j++) { - if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence."); + if (i + n >= src.size()) return ParseRes.error(src.loc(i), "Invalid hexadecimal escape sequence"); int val = fromHex(src.at(i + n)); - if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence."); + if (val == -1) throw new SyntaxException(src.loc(i + n), "Invalid hexadecimal escape sequence"); n++; newC = (newC << 4) | val; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Compiler.java b/src/main/java/me/topchetoeu/jscript/runtime/Compiler.java index 526e646..42266dc 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Compiler.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Compiler.java @@ -34,7 +34,7 @@ public interface Compiler { public static Compiler get(Environment ext) { return ext.get(KEY, (env, filename, src) -> { - throw EngineException.ofError("No compiler attached to engine."); + throw EngineException.ofError("No compiler attached to engine"); }); } -- 2.45.2 From 50eb204da73f0120a352b6d2bf911536c9c92392 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:04:19 +0200 Subject: [PATCH 17/41] fix: remove unnecessary reference from core to compiler --- .../me/topchetoeu/jscript/common/mapping/FunctionMap.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java b/src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java index 5b87d81..07f301c 100644 --- a/src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java +++ b/src/main/java/me/topchetoeu/jscript/common/mapping/FunctionMap.java @@ -15,7 +15,6 @@ import java.util.stream.Collectors; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.scope.Scope; public class FunctionMap { public static class FunctionMapBuilder { @@ -54,9 +53,6 @@ public class FunctionMap { public FunctionMap build(String[] localNames, String[] captureNames) { return new FunctionMap(sourceMap, breakpoints, localNames, captureNames); } - public FunctionMap build(Scope scope) { - return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]); - } public FunctionMap build() { return new FunctionMap(sourceMap, breakpoints, new String[0], new String[0]); } -- 2.45.2 From 5644966dd7c8e9398cecce8e14e7621300546d75 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:06:04 +0200 Subject: [PATCH 18/41] regress: remove ES6 nodes --- .../jscript/compilation/ClassNode.java | 244 ------------------ .../compilation/ClassStatementNode.java | 40 --- .../compilation/FunctionArrowNode.java | 78 ------ .../jscript/compilation/Parameters.java | 84 ------ .../compilation/control/ForOfNode.java | 109 -------- .../members/AssignShorthandNode.java | 57 ---- .../compilation/members/MethodMemberNode.java | 70 ----- .../compilation/patterns/AssignPattern.java | 84 ------ .../jscript/compilation/patterns/Binding.java | 80 ------ .../compilation/patterns/ObjectPattern.java | 161 ------------ .../jscript/compilation/patterns/Pattern.java | 59 ----- .../compilation/values/ClassValueNode.java | 34 --- 12 files changed, 1100 deletions(-) delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/ClassStatementNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/Parameters.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java deleted file mode 100644 index 3efad3b..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassNode.java +++ /dev/null @@ -1,244 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import java.util.ArrayList; -import java.util.LinkedList; -import java.util.List; -import java.util.function.Consumer; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.SyntaxException; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.environment.Environment; -import me.topchetoeu.jscript.common.environment.Key; -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.members.FieldMemberNode; -import me.topchetoeu.jscript.compilation.members.Member; -import me.topchetoeu.jscript.compilation.members.MethodMemberNode; -import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; -import me.topchetoeu.jscript.compilation.scope.FunctionScope; - -public abstract class ClassNode extends FunctionNode { - public static final class ClassBody { - public final List staticMembers; - public final List protoFields; - public final List protoMembers; - public final Parameters constructorParameters; - public final CompoundNode constructorBody; - public final Node superExpr; - public final boolean hasConstr; - - public ClassBody( - List staticMembers, List protoFields, List protoMembers, - Parameters constructorParameters, CompoundNode constructorBody, - Node superExpr, boolean hasConstr - ) { - this.staticMembers = staticMembers; - this.protoFields = protoFields; - this.protoMembers = protoMembers; - this.constructorParameters = constructorParameters; - this.constructorBody = constructorBody; - this.superExpr = superExpr; - this.hasConstr = hasConstr; - } - } - - public static final Key CLASS_ROOT = Key.of(); - public static final Key> SUPER = Key.of(); - public static final Key> SUPER_PROTO = Key.of(); - public static final Key> SUPER_CONSTR = Key.of(); - public static final Key> ON_SUPER_CALL = Key.of(); - - public final ClassBody body; - public final String name; - - @Override public String name() { return name; } - - public void compileStatic(CompileResult target) { - for (var member : body.staticMembers) { - member.compile(target, true, false); - } - } - public void compilePrototype(CompileResult target) { - if (body.protoMembers.size() > 0) { - target.add(Instruction.dup()); - target.add(Instruction.loadMember("prototype")); - - for (var i = 0; i < body.protoMembers.size() - 1; i++) { - body.protoMembers.get(i).compile(target, true, false); - } - - body.protoMembers.get(body.protoMembers.size() - 1).compile(target, false, false); - } - } - - private void compileFieldInits(CompileResult target) { - for (var member : body.protoFields) { - target.add(Instruction.loadThis()); - member.compile(target, false, true); - } - } - - @Override protected void compilePreBody(CompileResult target) { - if (target.env.hasNotNull(SUPER_PROTO)) { - if (!body.hasConstr) { - throw new SyntaxException(loc(), "Default constructors in derived classes not supported"); - // compileFieldInits(target); - } - } - else compileFieldInits(target); - } - - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - if (body.superExpr == null) { - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, false, true, false, false, name, captures(id, target))); - compileStatic(target); - compilePrototype(target); - } - else { - var subtarget = target.subtarget().rootEnvironment(JavaScript.COMPILE_ROOT); - subtarget.scope.singleEntry = true; - subtarget.beginScope(); - var protoVar = target.scope.defineTemp(); - var constrVar = target.scope.defineTemp(); - - subtarget.env.add(SUPER_PROTO, t -> { - var i = t.scope.get(protoVar, false); - t.add(_i -> i.index().toGet()); - }); - subtarget.env.add(SUPER_CONSTR, t -> { - var i = t.scope.get(constrVar, false); - t.add(_i -> i.index().toGet()); - }); - - var staticTarget = subtarget.subEnvironment(); - staticTarget.env.add(SUPER, subtarget.env.get(SUPER_CONSTR)); - staticTarget.env.add(CLASS_ROOT, staticTarget.env); - - var protoTarget = subtarget.subEnvironment(); - protoTarget.env.add(SUPER, subtarget.env.get(SUPER_PROTO)); - protoTarget.env.add(CLASS_ROOT, protoTarget.env); - - var constrEnv = subtarget.env.child(); - constrEnv.add(SUPER, subtarget.env.get(SUPER_PROTO)); - constrEnv.add(ON_SUPER_CALL, this::compileFieldInits); - constrEnv.add(CLASS_ROOT, constrEnv); - - var id = target.addChild(compileBody(constrEnv, new FunctionScope(subtarget.scope), false, name, null)); - target.add(_i -> Instruction.loadFunc(id, false, true, false, true, name, captures(id, target))); - - body.superExpr.compile(target, true); - - target.add(Instruction.extend()); - target.add(Instruction.dup(1, 0)); - target.add(Instruction.loadMember("prototype")); - target.add(_i -> protoVar.index().toInit()); - target.add(_i -> constrVar.index().toInit()); - - compileStatic(staticTarget); - compilePrototype(protoTarget); - subtarget.endScope(); - } - } - - public ClassNode(Location loc, Location end, String name, ClassBody body) { - super(loc, end, body.constructorParameters, body.constructorBody); - - this.name = name; - this.body = body; - } - - public static ParseRes parseMember(Source src, int i) { - return ParseRes.first(src, i, - PropertyMemberNode::parse, - FieldMemberNode::parseClass, - MethodMemberNode::parse - ); - } - - public static ParseRes parseBody(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - ParseRes superExpr = ParseRes.failed(); - - if (Parsing.isIdentifier(src, i + n, "extends")) { - n += 7; - - superExpr = JavaScript.parseExpression(src, i + n, 14); - if (!superExpr.isSuccess()) return superExpr.chainError(src.loc(i + n), "Expected an expression after 'extends'"); - n += superExpr.n; - n += Parsing.skipEmpty(src, i + n); - } - - if (!src.is(i + n, "{")) return ParseRes.error(src.loc(i + n), "Expected a class body"); - n++; - n += Parsing.skipEmpty(src, i + n); - - var fields = new LinkedList(); - var members = new LinkedList(); - var statics = new LinkedList(); - - var params = new Parameters(new ArrayList<>()); - var body = new CompoundNode(loc, false); - var hasConstr = false; - - if (src.is(i + n, "}")) { - n++; - return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, false), n); - } - - while (true) { - ParseRes prop = parseMember(src, i + n); - - if (prop.isSuccess()) { - n += prop.n; - - if (prop.result instanceof FieldMemberNode field) fields.add(field); - else if (prop.result instanceof MethodMemberNode method && method.name().equals("constructor")) { - if (hasConstr) return ParseRes.error(loc, "A class may only have one constructor"); - - params = method.params; - body = method.body; - hasConstr = true; - } - else members.add(prop.result); - } - else if (Parsing.isIdentifier(src, i + n, "static")) { - n += 6; - - var staticProp = parseMember(src, i + n); - if (!staticProp.isSuccess()) { - if (prop.isError()) return prop.chainError(); - else return staticProp.chainError(src.loc(i + n), "Expected a member after 'static' keyword"); - } - n += staticProp.n; - - statics.add(staticProp.result); - } - else { - var end = JavaScript.parseStatementEnd(src, i + n); - if (end.isSuccess()) n += end.n; - else return ParseRes.error(src.loc(i + n), "Expected a member, end of statement or a closing colon"); - } - - n += Parsing.skipEmpty(src, i + n); - - if (src.is(i + n, "}")) { - n++; - break; - } - // else return ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); - } - - return ParseRes.res(new ClassBody(statics, fields, members, params, body, superExpr.result, hasConstr), n); - } - - // public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { - // super(loc, end, params, body); - // this.name = name; - // } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/ClassStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/ClassStatementNode.java deleted file mode 100644 index eaeb32c..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/ClassStatementNode.java +++ /dev/null @@ -1,40 +0,0 @@ -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.common.parsing.ParseRes; -import me.topchetoeu.jscript.common.parsing.Parsing; -import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; - -public class ClassStatementNode extends ClassNode { - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - super.compile(target, pollute, name, bp); - var i = target.scope.define(DeclarationType.LET, name(), loc()); - target.add(_i -> i.index().toInit()); - if (pollute) target.add(Instruction.pushUndefined()); - } - - public ClassStatementNode(Location loc, Location end, String name, ClassBody body) { - super(loc, end, name, body); - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed(); - n += 5; - - var name = Parsing.parseIdentifier(src, i + n); - if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a class name"); - n += name.n; - - var body = parseBody(src, i + n); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a class body"); - n += body.n; - - return ParseRes.res(new ClassStatementNode(loc, src.loc(i + n), name.result, body.result), n); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java deleted file mode 100644 index d789622..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionArrowNode.java +++ /dev/null @@ -1,78 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import java.util.Arrays; - -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.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.control.ReturnNode; -import me.topchetoeu.jscript.compilation.patterns.Pattern; - -public class FunctionArrowNode extends FunctionNode { - @Override public String name() { return null; } - - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, true, false, null, captures(id, target))); - } - - @Override protected Environment rootEnv(Environment env) { - return env.getWith(ClassNode.CLASS_ROOT, () -> super.rootEnv(env)); - } - - public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) { - super(loc, end, params, expToBody(body)); - } - - private static final CompoundNode expToBody(Node node) { - if (node instanceof CompoundNode res) return res; - else return new CompoundNode(node.loc(), false, new ReturnNode(node.loc(), node)); - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - Parameters params; - - if (src.is(i + n, "(")) { - var paramsRes = Parameters.parseParameters(src, i + n); - if (!paramsRes.isSuccess()) return paramsRes.chainError(); - n += paramsRes.n; - n += Parsing.skipEmpty(src, i + n); - - params = paramsRes.result; - } - else { - var singleParam = Pattern.parse(src, i + n, true); - if (!singleParam.isSuccess()) return ParseRes.failed(); - n += singleParam.n; - n += Parsing.skipEmpty(src, i + n); - - params = new Parameters(Arrays.asList(singleParam.result)); - } - - if (!src.is(i + n, "=>")) return ParseRes.failed(); - n += 2; - n += Parsing.skipEmpty(src, i + n); - - if (src.is(i + n, "{")) { - var body = CompoundNode.parse(src, i + n); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); - n += body.n; - - return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); - } - else { - var body = JavaScript.parseExpression(src, i + n, 2); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compount statement after '=>'"); - n += body.n; - - return ParseRes.res(new FunctionArrowNode(loc, src.loc(i + n - 1), params, body.result), n); - } - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/Parameters.java b/src/main/java/me/topchetoeu/jscript/compilation/Parameters.java deleted file mode 100644 index 712a2e8..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/Parameters.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.topchetoeu.jscript.compilation; - -import java.util.ArrayList; -import java.util.List; - -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.patterns.Pattern; -import me.topchetoeu.jscript.compilation.values.operations.AssignNode; - -public final class Parameters { - public final int length; - public final List params; - public final Pattern rest; - - public Parameters(List params, Pattern rest) { - var len = params.size(); - - for (var i = params.size() - 1; i >= 0; i--) { - if (!(params.get(i) instanceof AssignNode)) break; - len--; - } - - this.params = params; - this.length = len; - this.rest = rest; - } - public Parameters(List params) { - this(params, null); - } - - public static ParseRes parseParameters(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - - var openParen = Parsing.parseOperator(src, i + n, "("); - if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list"); - n += openParen.n; - - var params = new ArrayList(); - - var closeParen = Parsing.parseOperator(src, i + n, ")"); - n += closeParen.n; - - if (!closeParen.isSuccess()) { - while (true) { - n += Parsing.skipEmpty(src, i + n); - - if (src.is(i + n, "...")) { - n += 3; - - var rest = Pattern.parse(src, i + n, true); - if (!rest.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a rest parameter"); - n += rest.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected an end of parameters list after rest parameter"); - n++; - - return ParseRes.res(new Parameters(params, rest.result), n); - } - - var param = Pattern.parse(src, i + n, true); - if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace"); - n += param.n; - n += Parsing.skipEmpty(src, i + n); - - params.add(param.result); - - if (src.is(i + n, ",")) { - n++; - n += Parsing.skipEmpty(src, i + n); - } - - if (src.is(i + n, ")")) { - n++; - break; - } - } - } - - return ParseRes.res(new Parameters(params), n); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java deleted file mode 100644 index 9bd3108..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForOfNode.java +++ /dev/null @@ -1,109 +0,0 @@ -package me.topchetoeu.jscript.compilation.control; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Instruction.BreakpointType; -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.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; -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.patterns.Binding; - -public class ForOfNode extends Node { - public final Binding binding; - public final Node iterable, body; - public final String label; - - @Override public void resolve(CompileResult target) { - body.resolve(target); - binding.resolve(target); - } - - @Override public void compile(CompileResult target, boolean pollute) { - binding.declareLateInit(target); - - iterable.compile(target, true, BreakpointType.STEP_OVER); - target.add(Instruction.dup()); - target.add(Instruction.loadIntrinsics("it_key")); - target.add(Instruction.loadMember()).setLocation(iterable.loc()); - target.add(Instruction.call(0, true)).setLocation(iterable.loc()); - - int start = target.size(); - target.add(Instruction.dup(2, 0)); - target.add(Instruction.loadMember("next")).setLocation(iterable.loc()); - target.add(Instruction.call(0, true)).setLocation(iterable.loc()); - target.add(Instruction.dup()); - target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); - int mid = target.temp(); - - target.add(Instruction.loadMember("value")).setLocation(binding.loc); - binding.assign(target, false); - - var end = new DeferredIntSupplier(); - - LabelContext.pushLoop(target.env, loc(), label, end, start); - CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(target.env, label); - - int endI = target.size(); - end.set(endI); - - target.add(Instruction.jmp(start - endI)); - target.add(Instruction.discard()); - target.add(Instruction.discard()); - target.set(mid, Instruction.jmpIf(endI - mid + 1)); - if (pollute) target.add(Instruction.pushUndefined()); - } - - public ForOfNode(Location loc, String label, Binding binding, Node object, Node body) { - super(loc); - this.label = label; - this.binding = binding; - this.iterable = object; - this.body = body; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var label = JavaScript.parseLabel(src, i + n); - n += label.n; - n += Parsing.skipEmpty(src, i + n); - - if (!Parsing.isIdentifier(src, i + n, "for")) return ParseRes.failed(); - n += 3; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, "(")) return ParseRes.error(src.loc(i + n), "Expected an opening paren"); - n++; - n += Parsing.skipEmpty(src, i + n); - - var binding = Binding.parse(src, i + n); - if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-of loop"); - n += binding.n; - n += Parsing.skipEmpty(src, i + n); - - if (!Parsing.isIdentifier(src, i + n, "of")) return ParseRes.error(src.loc(i + n), "Expected 'of' keyword after variable declaration"); - n += 2; - - var obj = JavaScript.parseExpression(src, i + n, 0); - if (!obj.isSuccess()) return obj.chainError(src.loc(i + n), "Expected a value"); - n += obj.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, ")")) return ParseRes.error(src.loc(i + n), "Expected a closing paren"); - n++; - - var bodyRes = JavaScript.parseStatement(src, i + n); - if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-of body"); - n += bodyRes.n; - - return ParseRes.res(new ForOfNode(loc, label.result, binding.result, obj.result, bodyRes.result), n); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java deleted file mode 100644 index 116f787..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/AssignShorthandNode.java +++ /dev/null @@ -1,57 +0,0 @@ -package me.topchetoeu.jscript.compilation.members; - -import me.topchetoeu.jscript.common.SyntaxException; -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.CompileResult; -import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.patterns.AssignTarget; -import me.topchetoeu.jscript.compilation.values.VariableNode; -import me.topchetoeu.jscript.compilation.values.constants.StringNode; -import me.topchetoeu.jscript.compilation.values.operations.AssignNode; - -public class AssignShorthandNode implements Member { - public final Location loc; - public final Node key; - public final AssignTarget target; - public final Node value; - - @Override public Location loc() { return loc; } - - @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { - throw new SyntaxException(loc(), "Unexpected assign shorthand in non-destructor context"); - } - - public AssignShorthandNode(Location loc, Node key, AssignTarget target, Node value) { - this.loc = loc; - this.key = key; - this.target = target; - this.value = value; - } - - public AssignTarget target() { - return new AssignNode(loc(), target, value); - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var var = VariableNode.parse(src, i + n); - if (!var.isSuccess()) return var.chainError(); - n += var.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, "=")) return ParseRes.failed(); - n++; - - var value = JavaScript.parseExpression(src, i + n, 2); - if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a shorthand initializer"); - n += value.n; - - return ParseRes.res(new AssignShorthandNode(loc, new StringNode(loc, var.result.name), var.result, value.result), n); - } -} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java deleted file mode 100644 index abc760e..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/MethodMemberNode.java +++ /dev/null @@ -1,70 +0,0 @@ -package me.topchetoeu.jscript.compilation.members; - -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.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.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; -import me.topchetoeu.jscript.compilation.FunctionNode; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.Parameters; -import me.topchetoeu.jscript.compilation.values.ObjectNode; -import me.topchetoeu.jscript.compilation.values.constants.StringNode; - -public class MethodMemberNode extends FunctionNode implements Member { - public final Node key; - - @Override public String name() { - if (key instanceof StringNode str) return str.value; - else return null; - } - - @Override protected Environment rootEnv(Environment env) { - return env; - } - - @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - if (pollute) target.add(Instruction.dup()); - key.compile(target, true); - - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target))); - } - - @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { - compile(target, pollute); - target.add(Instruction.defField(enumerable)); - } - - public MethodMemberNode(Location loc, Location end, Node key, Parameters params, CompoundNode body) { - super(loc, end, params, body); - this.key = key; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var name = ObjectNode.parsePropName(src, i + n); - if (!name.isSuccess()) return name.chainError(); - n += name.n; - - var params = Parameters.parseParameters(src, i + n); - if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); - n += params.n; - - var body = CompoundNode.parse(src, i + n); - if (!body.isSuccess()) return body.chainError(src.loc(i + n), "Expected a compound statement for property accessor."); - n += body.n; - - var end = src.loc(i + n - 1); - - return ParseRes.res(new MethodMemberNode( - loc, end, name.result, params.result, body.result - ), n); - } -} \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java deleted file mode 100644 index e6ea21a..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/AssignPattern.java +++ /dev/null @@ -1,84 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.Operation; -import me.topchetoeu.jscript.common.SyntaxException; -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.CompileResult; -import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; - -public class AssignPattern implements Pattern { - public final Location loc; - public final AssignTarget assignable; - public final Node value; - - @Override public Location loc() { return loc; } - - @Override public void destructDeclResolve(CompileResult target) { - if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - p.destructDeclResolve(target); - } - - private void common(CompileResult target) { - target.add(Instruction.dup()); - target.add(Instruction.pushUndefined()); - target.add(Instruction.operation(Operation.EQUALS)); - var start = target.temp(); - target.add(Instruction.discard()); - - value.compile(target, true); - - target.set(start, Instruction.jmpIfNot(target.size() - start)); - } - - @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { - if (lateInitializer) { - if (assignable instanceof Pattern p) p.declare(target, decl, lateInitializer); - else throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - } - else throw new SyntaxException(loc(), "Expected an assignment value for destructor declaration"); - } - @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { - if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - common(target); - p.destruct(target, decl, shouldDeclare); - } - - @Override public void beforeAssign(CompileResult target) { - assignable.beforeAssign(target); - } - @Override public void afterAssign(CompileResult target, boolean pollute) { - common(target); - assignable.afterAssign(target, false); - } - - public AssignPattern(Location loc, Pattern assignable, Node value) { - this.loc = loc; - this.assignable = assignable; - this.value = value; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var pattern = Pattern.parse(src, i + n, false); - if (!pattern.isSuccess()) return pattern.chainError(); - n += pattern.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, "=")) return ParseRes.failed(); - n++; - - var value = JavaScript.parseExpression(src, i + n, 2); - if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a default value"); - n += value.n; - - return ParseRes.res(new AssignPattern(loc, pattern.result, value.result), n); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java deleted file mode 100644 index bf7524f..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Binding.java +++ /dev/null @@ -1,80 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import me.topchetoeu.jscript.common.SyntaxException; -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.CompileResult; -import me.topchetoeu.jscript.compilation.JavaScript; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; - -public class Binding implements Pattern { - public final Location loc; - public final DeclarationType type; - public final AssignTarget assignable; - - @Override public Location loc() { return loc; } - - @Override public void destructDeclResolve(CompileResult target) { - if (type != null && !type.strict) { - if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - p.destructDeclResolve(target); - } - } - - @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { - if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - p.destruct(target, decl, shouldDeclare); - } - @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { - if (!(assignable instanceof Pattern p)) throw new SyntaxException(assignable.loc(), "Unexpected non-pattern in destruct context"); - p.declare(target, decl, lateInitializer); - } - - public void resolve(CompileResult target) { - if (type != null) destructDeclResolve(target); - } - - public void declare(CompileResult target, boolean hasInit) { - if (type != null) destructVar(target, type, hasInit); - } - public void declareLateInit(CompileResult target) { - if (type != null) declare(target, type, true); - } - - @Override public void afterAssign(CompileResult target, boolean pollute) { - assignable.assign(target, pollute); - } - - public Binding(Location loc, DeclarationType type, AssignTarget assignable) { - this.loc = loc; - this.type = type; - this.assignable = assignable; - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var declType = JavaScript.parseDeclarationType(src, i + n); - if (!declType.isSuccess()) { - var res = JavaScript.parseExpression(src, i + n, 13); - if (res.isSuccess() && res.result instanceof AssignTargetLike target) { - n += res.n; - return ParseRes.res(new Binding(loc, null, target.toAssignTarget()), n); - } - else return ParseRes.failed(); - } - else { - n += declType.n; - n += Parsing.skipEmpty(src, i + n); - - var res = Pattern.parse(src, i + n, false); - if (!res.isSuccess()) return ParseRes.failed(); - n += res.n; - - return ParseRes.res(new Binding(loc, declType.result, res.result), n); - } - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java deleted file mode 100644 index 65e508d..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/ObjectPattern.java +++ /dev/null @@ -1,161 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import java.util.LinkedList; -import java.util.List; -import java.util.function.Consumer; - -import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.SyntaxException; -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.CompileResult; -import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; -import me.topchetoeu.jscript.compilation.values.ObjectNode; -import me.topchetoeu.jscript.compilation.values.VariableNode; -import me.topchetoeu.jscript.compilation.values.constants.StringNode; -import me.topchetoeu.jscript.compilation.values.operations.IndexNode; - -public class ObjectPattern extends Node implements Pattern { - public static final class Member { - public final Node key; - public final AssignTarget consumable; - - public Member(Node key, AssignTarget consumer) { - this.key = key; - this.consumable = consumer; - } - } - - public final List members; - - public void compile(CompileResult target, Consumer consumer, boolean pollute) { - for (var el : members) { - target.add(Instruction.dup()); - IndexNode.indexLoad(target, el.key, true); - consumer.accept(el.consumable); - } - - if (!pollute) target.add(Instruction.discard()); - } - - @Override public void destructDeclResolve(CompileResult target) { - for (var t : members) { - if (t.consumable instanceof Pattern p) p.destructDeclResolve(target); - else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context"); - } - } - - @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { - compile(target, t -> { - if (t instanceof Pattern p) p.destruct(target, decl, shouldDeclare); - else throw new SyntaxException(t.loc(), "Unexpected non-pattern in destruct context"); - }, false); - } - - @Override public void afterAssign(CompileResult target, boolean pollute) { - compile(target, t -> t.assign(target, false), pollute); - } - - @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { - if (lateInitializer) { - for (var t : members) { - if (t.consumable instanceof Pattern p) p.declare(target, decl, lateInitializer); - else throw new SyntaxException(t.consumable.loc(), "Unexpected non-pattern in destruct context"); - } - } - else throw new SyntaxException(loc(), "Object pattern must be initialized"); - } - - public ObjectPattern(Location loc, List members) { - super(loc); - this.members = members; - } - - private static ParseRes parseShorthand(Source src, int i) { - ParseRes res = ParseRes.first(src, i, - AssignPattern::parse, - VariableNode::parse - ); - - if (res.isSuccess()) { - if (res.result instanceof AssignPattern assign) { - if (assign.assignable instanceof VariableNode var) { - return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n); - } - } - else if (res.result instanceof VariableNode var) { - return ParseRes.res(new Member(new StringNode(var.loc(), var.name), res.result), res.n); - } - } - - return res.chainError(); - } - private static ParseRes parseKeyed(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - - var key = ObjectNode.parsePropName(src, i + n); - if (!key.isSuccess()) return key.chainError(); - n += key.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n , ":")) return ParseRes.failed(); - n++; - - ParseRes res = Pattern.parse(src, i + n, true); - if (!res.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a pattern after colon"); - n += res.n; - - return ParseRes.res(new Member(key.result, res.result), n); - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!src.is(i + n, "{")) return ParseRes.failed(); - n++; - n += Parsing.skipEmpty(src, i + n); - - var members = new LinkedList(); - - if (src.is(i + n, "}")) { - n++; - return ParseRes.res(new ObjectPattern(loc, members), n); - } - - while (true) { - ParseRes prop = ParseRes.first(src, i + n, - ObjectPattern::parseKeyed, - ObjectPattern::parseShorthand - ); - - if (!prop.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a member in object pattern"); - n += prop.n; - - members.add(prop.result); - - n += Parsing.skipEmpty(src, i + n); - if (src.is(i + n, ",")) { - n++; - n += Parsing.skipEmpty(src, i + n); - - if (src.is(i + n, "}")) { - n++; - break; - } - - continue; - } - else if (src.is(i + n, "}")) { - n++; - break; - } - else ParseRes.error(src.loc(i + n), "Expected a comma or a closing brace."); - } - - return ParseRes.res(new ObjectPattern(loc, members), n); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java b/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java deleted file mode 100644 index 07d07fb..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/patterns/Pattern.java +++ /dev/null @@ -1,59 +0,0 @@ -package me.topchetoeu.jscript.compilation.patterns; - -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.common.parsing.ParseRes; -import me.topchetoeu.jscript.common.parsing.Source; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; -import me.topchetoeu.jscript.compilation.values.VariableNode; - -/** - * Represents all nodes that can be a destructors (note that all destructors are assign targets, too) - */ -public interface Pattern extends AssignTarget { - Location loc(); - - /** - * Called when the destructor has to declare - * @param target - */ - void destructDeclResolve(CompileResult target); - - /** - * Called when a declaration-like is being destructed - * @param decl The variable type the destructor must declare, if it is a named pne - */ - void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare); - - /** - * Run when destructing a declaration without an initializer - */ - void declare(CompileResult target, DeclarationType decl, boolean lateInitializer); - - public default void destructArg(CompileResult target, DeclarationType decl) { - destruct(target, decl, false); - } - public default void destructVar(CompileResult target, DeclarationType decl, boolean hasInitializer) { - if (hasInitializer) { - if (decl == null || !decl.strict) destruct(target, null, true); - else destruct(target, decl, true); - } - else { - if (decl == null || !decl.strict) declare(target, null, false); - else declare(target, decl, false); - } - } - - public static ParseRes parse(Source src, int i, boolean withDefault) { - return withDefault ? - ParseRes.first(src, i, - AssignPattern::parse, - ObjectPattern::parse, - VariableNode::parse - ) : - ParseRes.first(src, i, - ObjectPattern::parse, - VariableNode::parse - ); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java deleted file mode 100644 index e0240f4..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ClassValueNode.java +++ /dev/null @@ -1,34 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -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.ClassNode; -import me.topchetoeu.jscript.compilation.JavaScript; - -public class ClassValueNode extends ClassNode { - public ClassValueNode(Location loc, Location end, String name, ClassBody body) { - super(loc, end, name, body); - } - - public static ParseRes parse(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - if (!Parsing.isIdentifier(src, i + n, "class")) return ParseRes.failed(); - n += 5; - - var name = Parsing.parseIdentifier(src, i + n); - if (name.isSuccess() && !JavaScript.checkVarName(name.result)) { - name = ParseRes.error(src.loc(i + n), "Unexpected keyword '" + name.result + "'"); - } - n += name.n; - - var body = parseBody(src, i + n); - if (!body.isSuccess()) return body.chainError(name).chainError(src.loc(i + n), "Expected a class body"); - n += body.n; - - return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n); - } -} -- 2.45.2 From c5067cbfdd4d84e26fa99e45885ab8507f56b322 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:06:24 +0200 Subject: [PATCH 19/41] regress: remove ES6 variables and simplify scope --- .../compilation/scope/FunctionScope.java | 206 ++++++++++----- .../jscript/compilation/scope/Scope.java | 249 ------------------ .../jscript/compilation/scope/Variable.java | 6 - .../compilation/scope/VariableIndex.java | 19 +- .../compilation/scope/VariableList.java | 132 ++++------ 5 files changed, 205 insertions(+), 407 deletions(-) delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java 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 81850c3..8d38d50 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -1,64 +1,90 @@ package me.topchetoeu.jscript.compilation.scope; +import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; -import me.topchetoeu.jscript.common.parsing.Location; - -public class FunctionScope extends Scope { +public final class FunctionScope { + protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS); + protected final VariableList catches = new VariableList(VariableIndex.IndexType.LOCALS, this.locals); + protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this.catches); private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES); - private final HashMap specialVarMap = new HashMap<>(); - private final HashMap functionVarMap = new HashMap<>(); + private final HashMap localsMap = new HashMap<>(); private final HashMap capturesMap = new HashMap<>(); - private final HashSet blacklistNames = new HashSet<>(); + private final ArrayList catchesMap = new ArrayList<>(); private final HashMap childToParent = new HashMap<>(); private final HashMap parentToChild = new HashMap<>(); - private final Scope captureParent; + public final FunctionScope parent; + public final boolean passthrough; - public final boolean passtrough; - - @Override public boolean hasNonStrict(String name) { - if (functionVarMap.containsKey(name)) return true; - if (blacklistNames.contains(name)) return true; - - return false; + private Variable addCaptured(Variable var, boolean captured) { + if (captured && !this.capturables.has(var)) this.capturables.add(var); + return var; } - @Override public Variable define(Variable var, Location loc) { - checkNotEnded(); - if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name); + private Variable getCatchVar(String name) { + for (var el : catchesMap) { + if (el.name.equals(name)) return el; + } - if (passtrough) { - blacklistNames.add(var.name); - return null; - } + return null; + } + + /** + * @returns If a variable with the same name exists, the old variable. Otherwise, the given variable + */ + public Variable define(Variable var) { + if (passthrough) return null; else { - functionVarMap.put(var.name, var); + var old = get(var.name, false); + if (old != null) return old; + + localsMap.put(var.name, var); return locals.add(var); } } - public Variable defineSpecial(Variable var, Location loc) { - checkNotEnded(); - if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name); - specialVarMap.put(var.name, var); - return locals.add(var); + /** + * @returns A variable with the given name, or null if a global variable + */ + public Variable define(String name) { + return define(new Variable(name, false)); } - @Override public Variable get(String name, boolean capture) { - var superRes = super.get(name, capture); - if (superRes != null) return superRes; + /** + * Creates a catch variable and returns it + */ + public Variable defineCatch(String name) { + var var = new Variable(name, false); + this.catches.add(var); + this.catchesMap.add(var); + return var; + } + /** + * Removes the last catch variable. + * NOTE: the variable is still in the internal list. It just won't be findable by its name + */ + public void undefineCatch() { + this.catchesMap.remove(this.catchesMap.size() - 1); + } - if (specialVarMap.containsKey(name)) return addCaptured(specialVarMap.get(name), capture); - if (functionVarMap.containsKey(name)) return addCaptured(functionVarMap.get(name), capture); + /** + * Gets the index supplier of the given variable name, or null if it is a global + * + * @param capture If true, the variable is being captured by a function + */ + public Variable get(String name, boolean capture) { + var catchVar = getCatchVar(name); + + if (catchVar != null) return addCaptured(catchVar, capture); + if (localsMap.containsKey(name)) return addCaptured(localsMap.get(name), capture); if (capturesMap.containsKey(name)) return addCaptured(capturesMap.get(name), capture); - if (captureParent == null) return null; + if (parent == null) return null; - var parentVar = captureParent.get(name, true); + var parentVar = parent.get(name, true); if (parentVar == null) return null; var childVar = captures.add(parentVar.clone()); @@ -69,49 +95,67 @@ public class FunctionScope extends Scope { return childVar; } - @Override public Variable get(Variable var, boolean capture) { - if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture); + /** + * If the variable given is contained in this function, just returns the variable itself. + * However, this function is important to handle cases in which you might want to access + * a captured variable. In such cases, this function will return a capture to the given variable. + * + * @param capture Whether or not to execute this capturing logic + */ + public Variable get(Variable var, boolean capture) { + if (catches.has(var)) return addCaptured(var, capture); if (captures.has(var)) return addCaptured(var, capture); if (locals.has(var)) return addCaptured(var, capture); - if (captureParent == null) return null; - - var parentVar = captureParent.get(var, true); - if (parentVar == null) return null; - - var childVar = captures.add(parentVar.clone()); - childToParent.put(childVar, parentVar); - parentToChild.put(parentVar, childVar); - - return childVar; + if (capture) { + if (parentToChild.containsKey(var)) return addCaptured(parentToChild.get(var), capture); + + if (parent == null) return null; + + var parentVar = parent.get(var, true); + if (parentVar == null) return null; + + var childVar = captures.add(parentVar.clone()); + childToParent.put(childVar, parentVar); + parentToChild.put(parentVar, childVar); + + return childVar; + } + else return null; } - @Override public boolean has(String name, boolean capture) { - if (functionVarMap.containsKey(name)) return true; - if (specialVarMap.containsKey(name)) return true; + /** + * 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 (localsMap.containsKey(name)) return true; + // if (specialVarMap.containsKey(name)) return true; if (capture) { if (capturesMap.containsKey(name)) return true; - if (captureParent != null) return captureParent.has(name, true); + if (parent != null) return parent.has(name, true); } return false; } - @Override protected void onFinish() { - captures.freeze(); - super.onFinish(); + public int localsCount() { + return locals.size(); } - - @Override public int capturesCount() { + public int capturesCount() { return captures.size(); } + public int capturablesCount() { + return capturables.size(); + } public int[] getCaptureIndices() { var res = new int[captures.size()]; var i = 0; - for (var el : captures.all()) { + for (var el : captures) { assert childToParent.containsKey(el); res[i] = childToParent.get(el).index().toCaptureIndex(); i++; @@ -120,17 +164,43 @@ public class FunctionScope extends Scope { return res; } - public FunctionScope(Scope parent) { - super(); - if (parent.finished()) throw new RuntimeException("Parent is finished"); - this.captureParent = parent; - this.passtrough = false; - this.singleEntry = false; + public Iterable capturables() { + return capturables; } - public FunctionScope(boolean passtrough) { - super(); - this.captureParent = null; - this.passtrough = passtrough; - this.singleEntry = false; + public Iterable locals() { + return locals; + } + + public String[] captureNames() { + var res = new String[this.captures.size()]; + var i = 0; + + for (var el : this.captures) { + res[i++] = el.name; + } + + return res; + } + public String[] localNames() { + var res = new String[this.locals.size() + this.capturables.size()]; + var i = 0; + + for (var el : this.locals) { + res[i++] = el.name; + } + for (var el : this.capturables) { + res[i++] = el.name; + } + + return res; + } + + public FunctionScope(FunctionScope parent) { + this.parent = parent; + this.passthrough = false; + } + public FunctionScope(boolean passthrough) { + this.parent = null; + this.passthrough = passthrough; } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java deleted file mode 100644 index 92ffdd4..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Scope.java +++ /dev/null @@ -1,249 +0,0 @@ -package me.topchetoeu.jscript.compilation.scope; - -import java.util.HashMap; -import java.util.LinkedList; - -import me.topchetoeu.jscript.common.SyntaxException; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; - -public class Scope { - protected final HashMap strictVarMap = new HashMap<>(); - - protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS, this::variableOffset); - protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this::capturablesOffset); - - private boolean ended = false; - private boolean finished = false; - private Scope child; - private LinkedList children = new LinkedList<>(); - - public final Scope parent; - - /** - * 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; - - - protected void transferCaptured(Variable var) { - if (!singleEntry) { - if (!this.capturables.has(var)) this.capturables.add(var); - } - else if (parent != null) { - parent.transferCaptured(var); - } - else throw new IllegalStateException("Couldn't transfer captured variable"); - } - - protected final Variable addCaptured(Variable var, boolean captured) { - if (captured) transferCaptured(var); - return var; - } - - 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 a nameless variable for holding intermediate temporary values - * - * @throws RuntimeException If the scope is finalized or has an active child - */ - public Variable defineTemp() { - checkNotEnded(); - return this.locals.add(new Variable("", false)); - } - - /** - * 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 Variable define(Variable var, Location loc) { - checkNotEnded(); - if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name); - if (parent != null) return parent.define(var, loc); - - return null; - } - - /** - * Checks if this scope's function parent has a non-strict variable of the given name - */ - public boolean hasNonStrict(String name) { return false; } - - /** - * Defines an ES2015-style variable - * @param readonly True if const, false if let - * @return The index supplier of the variable - * @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 Variable defineStrict(Variable var, Location loc) { - checkNotEnded(); - if (strictVarMap.containsKey(var.name)) throw alreadyDefinedErr(loc, var.name); - if (hasNonStrict(var.name)) throw alreadyDefinedErr(loc, var.name); - - strictVarMap.put(var.name, var); - return locals.add(var); - } - /** - * Gets the index supplier of the given variable name, or null if it is a global - * - * @param capture If true, the variable is being captured by a function - */ - public Variable get(String name, boolean capture) { - var res = strictVarMap.get(name); - - if (res != null) return addCaptured(res, capture); - if (parent != null) return parent.get(name, capture); - - return null; - } - /** - * Gets the index supplier of the given variable, or null if it isn't in this scope chain. - * This will also execute capturing logic. - * - * @param capture If true, the variable is being captured by a function - */ - public Variable get(Variable var, boolean capture) { - if (locals.has(var)) return addCaptured(var, capture); - if (parent != null) return parent.get(var, 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 (strictVarMap.containsKey(name)) return true; - if (parent != null) return parent.has(name, capture); - - return false; - } - /** - * Gets the index offset from this scope to its children - */ - public final int variableOffset() { - var res = 0; - - for (var curr = parent; curr != null; curr = curr.parent) { - res += curr.locals.size(); - } - - return res; - } - public final int capturablesOffset() { - var res = 0; - - for (var curr = this; curr != null; curr = curr.parent) { - if (curr != this) res += curr.capturables.size(); - if (curr.parent == null) res += curr.localsCount(); - } - - return res; - } - - public final Variable define(DeclarationType type, String name, Location loc) { - if (type.strict) return defineStrict(new Variable(name, type.readonly), loc); - else return define(new Variable(name, type.readonly), loc); - } - - public int localsCount() { - var res = 0; - for (var child : children) { - var childN = child.localsCount(); - if (res < childN) res = childN; - } - - return res + locals.size(); - } - public int capturesCount() { return 0; } - public int allocCount() { - var res = capturables.size(); - return res; - } - public int capturablesCount() { - var res = capturables.size(); - for (var child : children) res += child.capturablesCount(); - return res; - } - - public Iterable capturables() { - return capturables.all(); - } - public Iterable locals() { - return locals.all(); - } - - /** - * 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; - - if (this.parent != null) { - assert this.parent.child == this; - this.parent.child = null; - } - - return true; - } - - protected void onFinish() { - this.locals.freeze(); - this.capturables.freeze(); - } - - /** - * Finalizes this scope. The scope will become immutable after this call - * @return - */ - public final boolean finish() { - if (finished) return false; - - if (parent != null && parent.finished) throw new IllegalStateException("Tried to finish a child after the parent was finished"); - this.onFinish(); - - for (var child : children) child.finish(); - - this.finished = true; - - return true; - } - - public final boolean ended() { return ended; } - public final boolean finished() { return finished; } - public final Scope child() { return child; } - - public Scope() { - this(null); - } - public Scope(Scope parent) { - 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.children.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 index 3a91792..571f796 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/Variable.java @@ -4,20 +4,14 @@ import java.util.function.Supplier; public final class Variable { private Supplier indexSupplier; - private boolean frozen; public final boolean readonly; public final String name; public final VariableIndex index() { - if (!frozen) throw new IllegalStateException("Tried to access the index of a variable before it was finalized"); return indexSupplier.get(); } - public final void freeze() { - this.frozen = true; - } - public final Variable setIndexSupplier(Supplier index) { this.indexSupplier = index; return this; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java index 374d7b1..278ab88 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java @@ -4,12 +4,21 @@ import me.topchetoeu.jscript.common.Instruction; public final class VariableIndex { public static enum IndexType { + /** + * A simple variable that is only ever used within the function + */ LOCALS, + /** + * A variable that has the ability to be captured by children functions + */ CAPTURABLES, + /** + * A variable that has been captured from the parent function + */ CAPTURES, } - public final VariableIndex.IndexType type; + public final IndexType type; public final int index; public final int toCaptureIndex() { @@ -44,14 +53,6 @@ public final class VariableIndex { default: throw new UnsupportedOperationException("Unknown index type " + type); } } - public final Instruction toUndefinedInit(boolean force) { - switch (type) { - case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type); - case CAPTURABLES: return Instruction.varInit(index, force); - case LOCALS: return Instruction.varInit(index, force); - default: throw new UnsupportedOperationException("Unknown index type " + type); - } - } public VariableIndex(VariableIndex.IndexType type, int index) { this.type = type; 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 5d8dcb5..9943083 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -1,48 +1,36 @@ package me.topchetoeu.jscript.compilation.scope; -import java.util.ArrayList; import java.util.HashMap; import java.util.Iterator; import java.util.function.IntSupplier; import java.util.function.Supplier; -public final class VariableList { +import me.topchetoeu.jscript.compilation.scope.VariableIndex.IndexType; + +public final class VariableList implements Iterable { private final class VariableNode implements Supplier { public Variable var; public VariableNode next; public VariableNode prev; - public boolean frozen; public int index; + public int indexIteration; public VariableList list() { return VariableList.this; } + private int getIndex() { + if (this.indexIteration != VariableList.this.indexIteration) { + this.indexIteration = VariableList.this.indexIteration; + + if (prev == null) this.index = 0; + else this.index = prev.getIndex() + 1; + } + + return this.index; + } + @Override public VariableIndex get() { - if (frozen) { - if (offset == null) return new VariableIndex(indexType, index); - else new VariableIndex(indexType, index + offset.getAsInt()); - } - - var res = 0; - if (offset != null) res = offset.getAsInt(); - - for (var it = prev; it != null; it = it.prev) { - res++; - } - - return new VariableIndex(indexType, res); - } - - public void freeze() { - if (frozen) return; - this.frozen = true; - this.next = null; - this.var.freeze(); - if (prev == null) return; - - this.index = prev.index + 1; - this.next = null; - - return; + if (offset == null) return new VariableIndex(indexType, this.getIndex()); + else return new VariableIndex(indexType, offset.getAsInt() + this.getIndex()); } public VariableNode(Variable var, VariableNode next, VariableNode prev) { @@ -54,32 +42,25 @@ public final class VariableList { private VariableNode first, last; - private ArrayList frozenList = null; private HashMap varMap = new HashMap<>(); private final IntSupplier offset; + /** + * Increased when indices need recalculation. VariableNode will check if + * its internal indexIteration is up to date with this, and if not, will + * recalculate its index + */ + private int indexIteration = 0; public final VariableIndex.IndexType indexType; - public boolean frozen() { - if (frozenList != null) { - assert frozenList != null; - assert varMap != null; - assert first == null; - assert last == null; - - return true; - } - else { - assert frozenList == null; - assert varMap != null; - - return false; - } - } - + /** + * Adds the given variable to this list. If it already exists, does nothing + * @return val + */ public Variable add(Variable val) { - if (frozen()) throw new RuntimeException("The scope has been frozen"); + if (this.varMap.containsKey(val)) return val; + this.indexIteration++; if (val.indexSupplier() instanceof VariableNode prevNode) { prevNode.list().remove(val); @@ -105,14 +86,18 @@ public final class VariableList { return val; } + /** + * If the variable is not in the list, does nothing. Otherwise, removes the variable from the list + * @return null if nothing was done, else the deleted variable (should be var) + */ public Variable remove(Variable var) { - if (frozen()) throw new RuntimeException("The scope has been frozen"); - if (var == null) return null; var node = varMap.get(var); if (node == null) return null; + this.indexIteration++; + if (node.prev != null) { assert node != first; node.prev.next = node.next; @@ -139,38 +124,26 @@ public final class VariableList { return node.var; } + /** + * Checks if the list has the given variable + */ public boolean has(Variable var) { return varMap.containsKey(var); } + /** + * Returns an indexer for the given variable + */ public Supplier indexer(Variable var) { return varMap.get(var); } public int size() { - if (frozen()) return frozenList.size(); - else return varMap.size(); + return varMap.size(); } - public void freeze() { - if (frozen()) return; - - frozenList = new ArrayList<>(); - - for (var node = first; node != null; ) { - frozenList.add(node); - - var tmp = node; - node = node.next; - tmp.freeze(); - } - - first = last = null; - } - - public Iterable all() { - if (frozen()) return () -> frozenList.stream().map(v -> v.var).iterator(); - else return () -> new Iterator() { + public Iterator iterator() { + return new Iterator() { private VariableNode curr = first; @Override public boolean hasNext() { @@ -186,19 +159,28 @@ public final class VariableList { }; } - public VariableList(VariableIndex.IndexType type, IntSupplier offset) { + /** + * @param offset Will offset the indices by the given amount from the supplier + */ + public VariableList(IndexType type, IntSupplier offset) { this.indexType = type; this.offset = offset; } - public VariableList(VariableIndex.IndexType type, int offset) { + /** + * @param offset Will offset the indices by the given amount + */ + public VariableList(IndexType type, int offset) { this.indexType = type; this.offset = () -> offset; } - public VariableList(VariableIndex.IndexType type, VariableList prev) { + /** + * @param offset Will offset the indices by the size of the given list + */ + public VariableList(IndexType type, VariableList prev) { this.indexType = type; this.offset = prev::size; } - public VariableList(VariableIndex.IndexType type) { + public VariableList(IndexType type) { this.indexType = type; this.offset = null; } -- 2.45.2 From 20f2c3c5e916403ce3d90497e535702d7ca26994 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:07:10 +0200 Subject: [PATCH 20/41] regress: remove ES6 stuff from members --- .../compilation/members/FieldMemberNode.java | 44 +----------- .../jscript/compilation/members/Member.java | 2 +- .../members/PropertyMemberNode.java | 34 ++++------ .../compilation/values/ObjectNode.java | 67 +------------------ 4 files changed, 21 insertions(+), 126 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java index 53edbf3..7888d94 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -9,8 +9,6 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.values.ObjectNode; -import me.topchetoeu.jscript.compilation.values.VariableNode; -import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class FieldMemberNode implements Member { public final Location loc; @@ -19,14 +17,14 @@ public class FieldMemberNode implements Member { @Override public Location loc() { return loc; } - @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.dup()); key.compile(target, true); if (value == null) target.add(Instruction.pushUndefined()); else value.compile(target, true); - target.add(Instruction.defField(enumerable)); + target.add(Instruction.defField()); } public FieldMemberNode(Location loc, Node key, Node value) { @@ -35,7 +33,7 @@ public class FieldMemberNode implements Member { this.value = value; } - public static ParseRes parseObject(Source src, int i) { + public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); var loc = src.loc(i + n); @@ -53,40 +51,4 @@ public class FieldMemberNode implements Member { return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); } - - public static ParseRes parseShorthand(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var var = VariableNode.parse(src, i + n); - if (!var.isSuccess()) return var.chainError(); - n += var.n; - - return ParseRes.res(new FieldMemberNode(loc, new StringNode(loc, var.result.name), var.result), n); - } - - public static ParseRes parseClass(Source src, int i) { - var n = Parsing.skipEmpty(src, i); - var loc = src.loc(i + n); - - var name = ObjectNode.parsePropName(src, i + n); - if (!name.isSuccess()) return name.chainError(); - n += name.n; - n += Parsing.skipEmpty(src, i + n); - - if (!src.is(i + n, "=")) { - var end = JavaScript.parseStatementEnd(src, i + n); - if (!end.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected an end of statement or a field initializer"); - n += end.n; - - return ParseRes.res(new FieldMemberNode(loc, name.result, null), n); - } - n++; - - var value = JavaScript.parseExpression(src, i + n, 2); - if (!value.isSuccess()) return value.chainError(src.loc(i + n), "Expected a value"); - n += value.n; - - return ParseRes.res(new FieldMemberNode(loc, name.result, value.result), n); - } } \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java index 505071a..8f48859 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java @@ -6,5 +6,5 @@ import me.topchetoeu.jscript.compilation.CompileResult; public interface Member { Location loc(); - void compile(CompileResult target, boolean pollute, boolean enumerable); + void compile(CompileResult target, boolean pollute); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java index 8290f73..8e0dcce 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -4,7 +4,6 @@ import java.util.Arrays; 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.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -12,15 +11,15 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.FunctionNode; +import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.Parameters; -import me.topchetoeu.jscript.compilation.patterns.Pattern; import me.topchetoeu.jscript.compilation.values.ObjectNode; +import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class PropertyMemberNode extends FunctionNode implements Member{ +public final class PropertyMemberNode extends FunctionNode implements Member { public final Node key; - public final Pattern argument; + public final VariableNode argument; @Override public String name() { if (key instanceof StringNode str) { @@ -30,10 +29,6 @@ public class PropertyMemberNode extends FunctionNode implements Member{ else return null; } - @Override protected Environment rootEnv(Environment env) { - return env; - } - public boolean isGetter() { return argument == null; } public boolean isSetter() { return argument != null; } @@ -42,17 +37,17 @@ public class PropertyMemberNode extends FunctionNode implements Member{ key.compile(target, true); var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, false, false, false, null, captures(id, target))); + target.add(Instruction.loadFunc(id, name(), captures(id, target))); } - @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) { - compile(target, pollute); - target.add(Instruction.defProp(isSetter(), enumerable)); + @Override public void compile(CompileResult target, boolean pollute) { + super.compile(target, pollute); + target.add(Instruction.defProp(isSetter())); } - public PropertyMemberNode(Location loc, Location end, Node key, Pattern argument, CompoundNode body) { - super(loc, end, argument == null ? new Parameters(Arrays.asList()) : new Parameters(Arrays.asList(argument)), body); + public PropertyMemberNode(Location loc, Location end, Node key, VariableNode argument, CompoundNode body) { + super(loc, end, argument == null ? Arrays.asList() : Arrays.asList(argument), body); this.key = key; this.argument = argument; } @@ -70,11 +65,10 @@ public class PropertyMemberNode extends FunctionNode implements Member{ if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a property name after '" + access + "'"); n += name.n; - var params = Parameters.parseParameters(src, i + n); + var params = JavaScript.parseParameters(src, i + n); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected an argument list"); - if (access.result.equals("get") && params.result.params.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters"); - if (access.result.equals("set") && params.result.params.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter"); - if (params.result.rest != null) return ParseRes.error(params.result.rest.loc(), "Property members may not have rest arguments"); + if (access.result.equals("get") && params.result.size() != 0) return ParseRes.error(src.loc(i + n), "Getter must not have any parameters"); + if (access.result.equals("set") && params.result.size() != 1) return ParseRes.error(src.loc(i + n), "Setter must have exactly one parameter"); n += params.n; var body = CompoundNode.parse(src, i + n); @@ -84,7 +78,7 @@ public class PropertyMemberNode extends FunctionNode implements Member{ var end = src.loc(i + n - 1); return ParseRes.res(new PropertyMemberNode( - loc, end, name.result, access.result.equals("get") ? null : params.result.params.get(0), body.result + loc, end, name.result, access.result.equals("get") ? null : params.result.get(0), body.result ), n); } } \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index b694117..8785416 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -4,7 +4,6 @@ import java.util.LinkedList; import java.util.List; import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -12,75 +11,18 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.members.AssignShorthandNode; import me.topchetoeu.jscript.compilation.members.FieldMemberNode; import me.topchetoeu.jscript.compilation.members.Member; -import me.topchetoeu.jscript.compilation.members.MethodMemberNode; import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; -import me.topchetoeu.jscript.compilation.patterns.AssignTarget; -import me.topchetoeu.jscript.compilation.patterns.AssignTargetLike; -import me.topchetoeu.jscript.compilation.patterns.ObjectPattern; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; -public class ObjectNode extends Node implements AssignTargetLike { +public class ObjectNode extends Node { public final List members; - // TODO: Implement spreading into object - - // private void compileRestObjBuilder(CompileResult target, int srcDupN) { - // var subtarget = target.subtarget(); - // var src = subtarget.scope.defineTemp(); - // var dst = subtarget.scope.defineTemp(); - - // target.add(Instruction.loadObj()); - // target.add(_i -> src.index().toSet(true)); - // target.add(_i -> dst.index().toSet(destructors.size() > 0)); - - // target.add(Instruction.keys(true, true)); - // var start = target.size(); - - // target.add(Instruction.dup()); - // var mid = target.temp(); - - // target.add(_i -> src.index().toGet()); - // target.add(Instruction.dup(1, 1)); - // target.add(Instruction.loadMember()); - - // target.add(_i -> dst.index().toGet()); - // target.add(Instruction.dup(1, 1)); - // target.add(Instruction.storeMember()); - - // target.add(Instruction.discard()); - // var end = target.size(); - // target.add(Instruction.jmp(start - end)); - // target.set(mid, Instruction.jmpIfNot(end - mid + 1)); - - // target.add(Instruction.discard()); - - // target.add(Instruction.dup(srcDupN, 1)); - - // target.scope.end(); - // } - @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); - for (var el : members) el.compile(target, true, true); - } - - @Override public AssignTarget toAssignTarget() { - var newMembers = new LinkedList(); - - for (var el : members) { - if (el instanceof FieldMemberNode field) { - if (field.value instanceof AssignTargetLike target) newMembers.add(new ObjectPattern.Member(field.key, target.toAssignTarget())); - else throw new SyntaxException(field.value.loc(), "Expected an assignable in deconstructor"); - } - else if (el instanceof AssignShorthandNode shorthand) newMembers.add(new ObjectPattern.Member(shorthand.key, shorthand.target())); - else throw new SyntaxException(el.loc(), "Unexpected member in deconstructor"); - } - - return new ObjectPattern(loc(), newMembers); + for (var el : members) el.compile(target, true); } public ObjectNode(Location loc, List map) { @@ -136,11 +78,8 @@ public class ObjectNode extends Node implements AssignTargetLike { while (true) { ParseRes prop = ParseRes.first(src, i + n, - MethodMemberNode::parse, PropertyMemberNode::parse, - FieldMemberNode::parseObject, - AssignShorthandNode::parse, - FieldMemberNode::parseShorthand + FieldMemberNode::parse ); if (!prop.isSuccess()) return prop.chainError(src.loc(i + n), "Expected a member in object literal"); n += prop.n; -- 2.45.2 From 754648fbf655ef76a8eea8c9c5094a28f4a22cc4 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:08:01 +0200 Subject: [PATCH 21/41] regress: remove ES6 instructions --- .../jscript/common/Instruction.java | 178 +++++++++--------- 1 file changed, 85 insertions(+), 93 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/Instruction.java b/src/main/java/me/topchetoeu/jscript/common/Instruction.java index 60251f9..f5cabb0 100644 --- a/src/main/java/me/topchetoeu/jscript/common/Instruction.java +++ b/src/main/java/me/topchetoeu/jscript/common/Instruction.java @@ -17,10 +17,7 @@ public class Instruction { TRY_END(0x06), CALL(0x10), - @Deprecated - CALL_MEMBER(0x11), CALL_NEW(0x12), - CALL_SUPER(0x13), JMP_IF(0x18), JMP_IFN(0x19), JMP(0x1A), @@ -40,11 +37,12 @@ public class Instruction { LOAD_GLOB(0x38), LOAD_INTRINSICS(0x39), - LOAD_ARGS(0x3A), - LOAD_REST_ARGS(0x3B), - LOAD_CALLEE(0x3C), - LOAD_THIS(0x3D), - LOAD_ERROR(0x3E), + LOAD_ARG(0x3A), + LOAD_ARGS_N(0x3B), + LOAD_ARGS(0x3C), + LOAD_CALLED(0x3D), + LOAD_THIS(0x3E), + LOAD_ERROR(0x3F), LOAD_VAR(0x40), LOAD_MEMBER(0x41), @@ -61,16 +59,10 @@ public class Instruction { KEYS(0x52), TYPEOF(0x53), OPERATION(0x54), - EXTEND(0x55), GLOB_GET(0x60), GLOB_SET(0x61), - GLOB_DEF(0x62), - - // CAP_INIT(0x70), - VAR_INIT(0x71), - CAP_FREE(0x72), - VAR_FREE(0x73); + GLOB_DEF(0x62); private static final HashMap types = new HashMap<>(); public final int numeric; @@ -88,8 +80,19 @@ public class Instruction { } } public static enum BreakpointType { + /** + * A debugger should never stop at such instruction, unless a breakpoint has been set on it + */ NONE, + /** + * Debuggers should pause at instructions marked with this breakpoint type + * after any step command + */ STEP_OVER, + /** + * Debuggers should pause at instructions marked with this breakpoint type + * only after a step-in command + */ STEP_IN; public boolean shouldStepIn() { @@ -108,27 +111,6 @@ public class Instruction { if (i >= params.length || i < 0) return null; return (T)params[i]; } - @SuppressWarnings("unchecked") - public T get(int i, T defaultVal) { - if (i >= params.length || i < 0) return defaultVal; - return (T)params[i]; - } - public boolean match(Object ...args) { - if (args.length != params.length) return false; - for (int i = 0; i < args.length; i++) { - var a = params[i]; - var b = args[i]; - if (a == null || b == null) { - if (!(a == null && b == null)) return false; - } - if (!a.equals(b)) return false; - } - return true; - } - public boolean is(int i, Object arg) { - if (params.length <= i) return false; - return params[i].equals(arg); - } // public void write(DataOutputStream writer) throws IOException { // var rawType = type.numeric; @@ -253,60 +235,79 @@ public class Instruction { // } // } + /** + * Signals the start of a protected context + * @param catchStart The point to witch to jump if an error has been caught + * @param finallyStart The point to witch to jump after either the try or catch bodies have exited + * @param end The point to which to jump after exiting the whole protected context + */ public static Instruction tryStart(int catchStart, int finallyStart, int end) { return new Instruction(Type.TRY_START, catchStart, finallyStart, end); } + /** + * Signifies that the current protected section (try, catch or finally) has ended + */ public static Instruction tryEnd() { return new Instruction(Type.TRY_END); } + /** + * Throws the top stack value + */ public static Instruction throwInstr() { return new Instruction(Type.THROW); } + /** + * Converts the given exception to a runtime syntax error and throws it + */ public static Instruction throwSyntax(SyntaxException err) { return new Instruction(Type.THROW_SYNTAX, err.getMessage()); } + /** + * Converts the given exception to a runtime syntax error and throws it + */ public static Instruction throwSyntax(String err) { return new Instruction(Type.THROW_SYNTAX, err); } + /** + * Converts the given exception to a runtime syntax error and throws it + */ public static Instruction throwSyntax(Location loc, String err) { return new Instruction(Type.THROW_SYNTAX, new SyntaxException(loc, err).getMessage()); } + /** + * Performs a JS object property deletion. + * Operands: + * 1. Object to manipulate + * 2. Key to delete + */ public static Instruction delete() { return new Instruction(Type.DELETE); } + /** + * Returns the top stack value + */ public static Instruction ret() { return new Instruction(Type.RETURN); } + /** + * A special NOP instruction telling any debugger to pause + */ public static Instruction debug() { return new Instruction(Type.NOP, "debug"); } + /** + * Does nothing. May be used for metadata or implementation-specific instructions that don't alter the behavior + */ public static Instruction nop(Object ...params) { return new Instruction(Type.NOP, params); } - public static Instruction call(int argn, boolean hasSelf, String name) { - return new Instruction(Type.CALL, argn, hasSelf, name); - } public static Instruction call(int argn, boolean hasSelf) { - return call(argn, hasSelf, ""); - } - @Deprecated - public static Instruction callMember(int argn, String name) { - return new Instruction(Type.CALL_MEMBER, argn, name); - } - @Deprecated - public static Instruction callMember(int argn) { - return new Instruction(Type.CALL_MEMBER, argn, ""); - } - public static Instruction callNew(int argn, String name) { - return new Instruction(Type.CALL_NEW, argn, name); + return new Instruction(Type.CALL, argn, hasSelf); } public static Instruction callNew(int argn) { - return new Instruction(Type.CALL_NEW, argn, ""); - } - public static Instruction callSuper(int argn) { - return new Instruction(Type.CALL_SUPER, argn); + return new Instruction(Type.CALL_NEW, argn); } public static Instruction jmp(int offset) { @@ -362,14 +363,30 @@ public class Instruction { public static Instruction loadThis() { return new Instruction(Type.LOAD_THIS); } - public static Instruction loadArgs(boolean real) { - return new Instruction(Type.LOAD_ARGS, real); + /** + * Loads the given argument + * @param i The index of the argument to load. If -1, will get the index from the stack instead + */ + public static Instruction loadArg(int i) { + return new Instruction(Type.LOAD_ARG, i); } - public static Instruction loadRestArgs(int offset) { - return new Instruction(Type.LOAD_REST_ARGS, offset); + /** + * Pushes the amount of arguments to the stack + */ + public static Instruction loadArgsN() { + return new Instruction(Type.LOAD_ARGS_N); } - public static Instruction loadCallee() { - return new Instruction(Type.LOAD_CALLEE); + /** + * Pushes the arguments object to the stack + */ + public static Instruction loadArgs() { + return new Instruction(Type.LOAD_ARGS); + } + /** + * Loads a reference to the function being called + */ + public static Instruction loadCalled() { + return new Instruction(Type.LOAD_CALLED); } public static Instruction loadGlob() { return new Instruction(Type.LOAD_GLOB); @@ -394,17 +411,11 @@ public class Instruction { return new Instruction(Type.LOAD_REGEX, pattern, flags); } // TODO: make this capturing a concern of the compiler - public static Instruction loadFunc(int id, boolean callable, boolean constructible, boolean captureThis, boolean noThis, String name, int[] captures) { - if (name == null) name = ""; - - var args = new Object[6 + captures.length]; + public static Instruction loadFunc(int id, String name, int[] captures) { + var args = new Object[2 + captures.length]; args[0] = id; args[1] = name; - args[2] = callable; - args[3] = constructible; - args[4] = captureThis; - args[5] = noThis; - for (var i = 0; i < captures.length; i++) args[i + 6] = captures[i]; + for (var i = 0; i < captures.length; i++) args[i + 2] = captures[i]; return new Instruction(Type.LOAD_FUNC, args); } public static Instruction loadObj() { @@ -463,36 +474,17 @@ public class Instruction { return new Instruction(Type.KEYS, own, onlyEnumerable); } - public static Instruction defProp(boolean setter, boolean enumerable) { - return new Instruction(Type.DEF_PROP, setter, enumerable); + public static Instruction defProp(boolean setter) { + return new Instruction(Type.DEF_PROP, setter); } - public static Instruction defField(boolean enumerable) { - return new Instruction(Type.DEF_FIELD, enumerable); - } - public static Instruction extend() { - return new Instruction(Type.EXTEND); + public static Instruction defField() { + return new Instruction(Type.DEF_FIELD); } public static Instruction operation(Operation op) { return new Instruction(Type.OPERATION, op); } - public static Instruction capFree(int i) { - return new Instruction(Type.CAP_FREE, i); - } - public static Instruction varFree(int i) { - return new Instruction(Type.VAR_FREE, i); - } - public static Instruction varInit(int i, boolean force) { - return new Instruction(Type.VAR_INIT, i, force); - } - // public static Instruction stackAlloc(int start, int n) { - // return new Instruction(Type.STACK_ALLOC, start, start + n); - // } - // public static Instruction stackRealloc(int start, int n) { - // return new Instruction(Type.STACK_REALLOC, start, start + n); - // } - @Override public String toString() { var res = type.toString(); -- 2.45.2 From 14e4aade351fdf07f94fdaffe9222805155d8b84 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:09:29 +0200 Subject: [PATCH 22/41] regress: remove infrastructure needed for ES6 stuff, simplify loops --- .../jscript/compilation/CompileResult.java | 72 +++---------------- .../jscript/compilation/CompoundNode.java | 21 ++---- .../jscript/compilation/JavaScript.java | 72 ++++++++++++------- .../jscript/compilation/LabelContext.java | 35 +++++++-- .../compilation/control/BreakNode.java | 4 +- .../compilation/control/ContinueNode.java | 4 +- .../compilation/control/DoWhileNode.java | 3 +- .../compilation/control/ForInNode.java | 34 +++++---- .../jscript/compilation/control/ForNode.java | 8 +-- .../compilation/control/SwitchNode.java | 34 ++++----- .../jscript/compilation/control/TryNode.java | 17 ++--- .../compilation/control/WhileNode.java | 2 +- 12 files changed, 140 insertions(+), 166 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index c0fb4e1..89e5b76 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -3,8 +3,6 @@ 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; import me.topchetoeu.jscript.common.Instruction; @@ -14,7 +12,7 @@ import me.topchetoeu.jscript.common.environment.Key; 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.Scope; +import me.topchetoeu.jscript.compilation.scope.FunctionScope; public final class CompileResult { public static final class ChildData { @@ -27,15 +25,12 @@ public final class CompileResult { } } - public final List> instructions; + public final List instructions; 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 final FunctionScope scope; public int temp() { instructions.add(null); @@ -43,21 +38,14 @@ public final class CompileResult { } public CompileResult add(Instruction instr) { - instructions.add(i -> instr); - return this; - } - public CompileResult add(IntFunction instr) { instructions.add(instr); return this; } public CompileResult set(int i, Instruction instr) { - instructions.set(i, _i -> instr); - return this; - } - public CompileResult set(int i, IntFunctioninstr) { instructions.set(i, instr); return this; } + public int size() { return instructions.size(); } public void setDebug(Location loc, BreakpointType type) { @@ -79,62 +67,25 @@ public final class CompileResult { setLocationAndDebug(instructions.size() - 1, loc, type); } - public void beginScope() { - // for (var cap : scope.capturables()) { - // add(_i -> Instruction.capInit(cap.index().index)); - // } - } - public void reallocScope() { - for (var cap : scope.capturables()) { - add(_i -> cap.index().toGet()); - add(_i -> Instruction.capFree(cap.index().index)); - add(_i -> cap.index().toInit()); - } - - scope.end(); - } - public void endScope() { - for (var cap : scope.capturables()) { - add(_i -> Instruction.capFree(cap.index().index)); - } - for (var var : scope.locals()) { - add(_i -> Instruction.varFree(var.index().index)); - } - - scope.end(); - } - public int addChild(CompileResult res) { this.children.add(res); return this.children.size() - 1; } public Instruction[] instructions() { - var res = new Instruction[instructions.size()]; - var i = 0; - for (var suppl : instructions) { - res[i] = suppl.apply(i); - i++; - } - return res; + return instructions.toArray(new Instruction[0]); } public FunctionMap map() { - return map.build(scope); + return map.build(scope.localNames(), scope.captureNames()); } public FunctionBody body() { var builtChildren = new FunctionBody[children.size()]; - for (var i = 0; i < children.size(); i++) builtChildren[i] = children.get(i).body(); - var instrRes = new Instruction[instructions.size()]; - var i = 0; + var instrRes = instructions(); - for (var suppl : instructions) { - instrRes[i] = suppl.apply(i); - // System.out.println(instrRes[i]); - i++; - } + for (var instr : instrRes) System.out.println(instr); return new FunctionBody( scope.localsCount(), scope.capturablesCount(), scope.capturesCount(), @@ -143,7 +94,7 @@ public final class CompileResult { } public CompileResult subtarget() { - return new CompileResult(env, new Scope(scope), this); + return new CompileResult(env, new FunctionScope(scope), this); } public CompileResult setEnvironment(Environment env) { @@ -160,16 +111,15 @@ public final class CompileResult { return new CompileResult(env.child(), scope, this); } - public CompileResult(Environment env, Scope scope, int length, Consumer task) { + public CompileResult(Environment env, FunctionScope scope, int length) { this.scope = scope; 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(Environment env, Scope scope, CompileResult parent) { + private CompileResult(Environment env, FunctionScope scope, CompileResult parent) { this.scope = scope; this.instructions = parent.instructions; this.children = parent.children; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index b99a75f..66b2e6d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -14,7 +14,6 @@ import me.topchetoeu.jscript.common.parsing.Source; public class CompoundNode extends Node { public final Node[] statements; - public boolean hasScope; public Location end; @Override public void resolve(CompileResult target) { @@ -24,12 +23,9 @@ public class CompoundNode extends Node { public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) { List statements = new ArrayList(); - var subtarget = hasScope ? target.subtarget() : target; - if (hasScope) subtarget.beginScope(); - for (var stm : this.statements) { if (stm instanceof FunctionStatementNode func) { - func.compile(subtarget, false); + func.compile(target, false); } else statements.add(stm); } @@ -39,12 +35,10 @@ public class CompoundNode extends Node { for (var i = 0; i < statements.size(); i++) { var stm = statements.get(i); - if (i != statements.size() - 1) stm.compile(subtarget, false, BreakpointType.STEP_OVER); - else stm.compile(subtarget, polluted = pollute, BreakpointType.STEP_OVER); + if (i != statements.size() - 1) stm.compile(target, false, BreakpointType.STEP_OVER); + else stm.compile(target, polluted = pollute, BreakpointType.STEP_OVER); } - if (hasScope) subtarget.endScope(); - if (!polluted && pollute) { target.add(Instruction.pushUndefined()); } @@ -59,9 +53,8 @@ public class CompoundNode extends Node { return this; } - public CompoundNode(Location loc, boolean hasScope, Node ...statements) { + public CompoundNode(Location loc, Node ...statements) { super(loc); - this.hasScope = hasScope; this.statements = statements; } @@ -92,9 +85,9 @@ public class CompoundNode extends Node { children.addAll(Arrays.asList(comp.statements)); children.add(curr.result); - return ParseRes.res(new CompoundNode(loc, comp.hasScope, children.toArray(new Node[0])), n); + return ParseRes.res(new CompoundNode(loc, children.toArray(new Node[0])), n); } - else return ParseRes.res(new CompoundNode(loc, false, prev, curr.result), n); + else return ParseRes.res(new CompoundNode(loc, prev, curr.result), n); } public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); @@ -124,6 +117,6 @@ public class CompoundNode extends Node { statements.add(res.result); } - return ParseRes.res(new CompoundNode(loc, true, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n); + return ParseRes.res(new CompoundNode(loc, statements.toArray(new Node[0])).setEnd(src.loc(i + n - 1)), n); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 5110a11..8217fa6 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; +import java.util.List; import java.util.Set; import me.topchetoeu.jscript.common.SyntaxException; @@ -17,9 +18,7 @@ import me.topchetoeu.jscript.compilation.control.ContinueNode; import me.topchetoeu.jscript.compilation.control.DebugNode; import me.topchetoeu.jscript.compilation.control.DeleteNode; import me.topchetoeu.jscript.compilation.control.DoWhileNode; -import me.topchetoeu.jscript.compilation.control.ForInNode; import me.topchetoeu.jscript.compilation.control.ForNode; -import me.topchetoeu.jscript.compilation.control.ForOfNode; import me.topchetoeu.jscript.compilation.control.IfNode; import me.topchetoeu.jscript.compilation.control.ReturnNode; import me.topchetoeu.jscript.compilation.control.SwitchNode; @@ -29,7 +28,6 @@ import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.FunctionScope; import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; -import me.topchetoeu.jscript.compilation.values.ClassValueNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; import me.topchetoeu.jscript.compilation.values.SuperNode; @@ -49,16 +47,8 @@ import me.topchetoeu.jscript.compilation.values.operations.TypeofNode; public final class JavaScript { public static enum DeclarationType { - VAR(false, false), - CONST(true, true), - LET(true, false); - - public final boolean strict, readonly; - - private DeclarationType(boolean strict, boolean readonly) { - this.strict = strict; - this.readonly = readonly; - } + @Deprecated + VAR; } public static final Key COMPILE_ROOT = Key.of(); @@ -93,7 +83,6 @@ public final class JavaScript { return ParseRes.first(src, i, (s, j) -> statement ? ParseRes.failed() : ObjectNode.parse(s, j), (s, j) -> statement ? ParseRes.failed() : FunctionNode.parseFunction(s, j, false), - (s, j) -> statement ? ParseRes.failed() : ClassValueNode.parse(s, j), JavaScript::parseLiteral, StringNode::parse, RegexNode::parse, @@ -102,7 +91,6 @@ public final class JavaScript { ChangeNode::parsePrefixIncrease, OperationNode::parsePrefix, ArrayNode::parse, - (s, j) -> statement ? ParseRes.failed() : FunctionArrowNode.parse(s, j), JavaScript::parseParens, CallNode::parseNew, TypeofNode::parse, @@ -188,14 +176,13 @@ public final class JavaScript { return res.addN(end.n); } - public static ParseRes parseStatement(Source src, int i) { + public static ParseRes parseStatement(Source src, int i) { var n = Parsing.skipEmpty(src, i); if (src.is(i + n, ";")) return ParseRes.res(new DiscardNode(src.loc(i+ n), null), n + 1); if (Parsing.isIdentifier(src, i + n, "with")) return ParseRes.error(src.loc(i + n), "'with' statements are not allowed."); - ParseRes res = ParseRes.first(src, i + n, - ClassStatementNode::parse, + ParseRes res = ParseRes.first(src, i + n, VariableDeclareNode::parse, ReturnNode::parse, ThrowNode::parse, @@ -206,8 +193,6 @@ public final class JavaScript { WhileNode::parse, SwitchNode::parse, ForNode::parse, - ForInNode::parse, - ForOfNode::parse, DoWhileNode::parse, TryNode::parse, CompoundNode::parse, @@ -231,13 +216,11 @@ public final class JavaScript { return ParseRes.failed(); } - public static ParseRes parseDeclarationType(Source src, int i) { + public static ParseRes parseDeclarationType(Source src, int i) { var res = Parsing.parseIdentifier(src, i); if (!res.isSuccess()) return res.chainError(); - if (res.result.equals("var")) return ParseRes.res(DeclarationType.VAR, res.n); - if (res.result.equals("let")) return ParseRes.res(DeclarationType.LET, res.n); - if (res.result.equals("const")) return ParseRes.res(DeclarationType.CONST, res.n); + if (res.result.equals("var")) return ParseRes.res(true, res.n); return ParseRes.failed(); } @@ -272,9 +255,8 @@ public final class JavaScript { env = env.child(); env.add(COMPILE_ROOT, env); - var func = new FunctionValueNode(null, null, new Parameters(Arrays.asList()), new CompoundNode(null, true, statements), null); + var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null); var res = func.compileBody(env, new FunctionScope(true), true, null, null); - res.buildTask.run(); return res; } @@ -299,4 +281,42 @@ public final class JavaScript { return ParseRes.res(nameRes.result, n); } + + public static ParseRes> parseParameters(Source src, int i) { + var n = Parsing.skipEmpty(src, i); + + var openParen = Parsing.parseOperator(src, i + n, "("); + if (!openParen.isSuccess()) return openParen.chainError(src.loc(i + n), "Expected a parameter list"); + n += openParen.n; + + var params = new ArrayList(); + + var closeParen = Parsing.parseOperator(src, i + n, ")"); + n += closeParen.n; + + if (!closeParen.isSuccess()) { + while (true) { + n += Parsing.skipEmpty(src, i + n); + + var param = VariableNode.parse(src, i + n); + if (!param.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a parameter or a closing brace"); + n += param.n; + n += Parsing.skipEmpty(src, i + n); + + params.add(param.result); + + if (src.is(i + n, ",")) { + n++; + n += Parsing.skipEmpty(src, i + n); + } + + if (src.is(i + n, ")")) { + n++; + break; + } + } + } + + return ParseRes.res(params, n); + } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java b/src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java index 22f371c..a9cfe03 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/LabelContext.java @@ -1,8 +1,9 @@ package me.topchetoeu.jscript.compilation; +import java.util.ArrayList; import java.util.HashMap; import java.util.LinkedList; -import java.util.function.IntFunction; +import java.util.Stack; import java.util.function.IntSupplier; import me.topchetoeu.jscript.common.Instruction; @@ -18,6 +19,8 @@ public class LabelContext { private final LinkedList list = new LinkedList<>(); private final HashMap map = new HashMap<>(); + private final Stack> deferredAdders = new Stack<>(); + public IntSupplier get() { return list.peekLast(); } @@ -25,15 +28,31 @@ public class LabelContext { return map.get(name); } - public IntFunction getJump() { + public void flushAdders() { + for (var adder : deferredAdders.peek()) { + adder.run(); + } + + deferredAdders.pop(); + } + + public boolean jump(CompileResult target) { var res = get(); - if (res == null) return null; - else return i -> Instruction.jmp(res.getAsInt() - i); + if (res != null) { + var tmp = target.temp(); + this.deferredAdders.peek().add(() -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp))); + return true; + } + else return false; } - public IntFunction getJump(String name) { + public boolean jump(CompileResult target, String name) { var res = get(name); - if (res == null) return null; - else return i -> Instruction.jmp(res.getAsInt() - i); + if (res != null) { + var tmp = target.temp(); + this.deferredAdders.peek().add(() -> target.set(tmp, Instruction.jmp(res.getAsInt() - tmp))); + return true; + } + else return false; } public void push(IntSupplier jumpTarget) { @@ -48,6 +67,7 @@ public class LabelContext { public void pushLoop(Location loc, String name, IntSupplier jumpTarget) { push(jumpTarget); push(loc, name, jumpTarget); + deferredAdders.push(new ArrayList<>()); } public void pop() { @@ -61,6 +81,7 @@ public class LabelContext { public void popLoop(String name) { pop(); pop(name); + flushAdders(); } public static LabelContext getBreak(Environment env) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index ee2c7aa..bd2e877 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -15,12 +15,10 @@ public class BreakNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - var res = LabelContext.getBreak(target.env).getJump(); - if (res == null) { + if (!LabelContext.getBreak(target.env).jump(target)) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); else throw new SyntaxException(loc(), "Illegal break statement"); } - target.add(res); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index 2b8a677..f0b13ed 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -15,12 +15,10 @@ public class ContinueNode extends Node { public final String label; @Override public void compile(CompileResult target, boolean pollute) { - var res = LabelContext.getCont(target.env).getJump(); - if (res == null) { + if (!LabelContext.getCont(target.env).jump(target)) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); else throw new SyntaxException(loc(), "Illegal continue statement"); } - target.add(res); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index 1f3de82..6f64061 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -27,13 +27,14 @@ public class DoWhileNode extends Node { LabelContext.pushLoop(target.env, loc(), label, end, start); body.compile(target, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(target.env, label); mid.set(target.size()); condition.compile(target, true, BreakpointType.STEP_OVER); int endI = target.size(); end.set(endI + 1); + LabelContext.popLoop(target.env, label); + target.add(Instruction.jmpIf(start - endI)); } 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 454cfb3..17de917 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -8,14 +8,15 @@ import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompoundNode; -import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; +import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.patterns.Binding; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class ForInNode extends Node { - public final Binding binding; + public final boolean isDecl; + public final VariableNode binding; public final Node object, body; public final String label; @@ -25,8 +26,6 @@ public class ForInNode extends Node { } @Override public void compile(CompileResult target, boolean pollute) { - binding.declareLateInit(target); - object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(false, true)); @@ -35,27 +34,32 @@ public class ForInNode extends Node { int mid = target.temp(); target.add(Instruction.loadMember("value")).setLocation(binding.loc()); - binding.assign(target, false); + target.add(VariableNode.toInit(target, loc(), binding.name)); + target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(target.env, label); int endI = target.size(); target.add(Instruction.jmp(start - endI)); target.add(Instruction.discard()); target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); + + end.set(endI); + LabelContext.popLoop(target.env, label); + if (pollute) target.add(Instruction.pushUndefined()); } - public ForInNode(Location loc, String label, Binding binding, Node object, Node body) { + public ForInNode(Location loc, String label, VariableNode binding, boolean isDecl, Node object, Node body) { super(loc); this.label = label; this.binding = binding; + this.isDecl = isDecl; this.object = object; this.body = body; } @@ -76,9 +80,15 @@ public class ForInNode extends Node { n++; n += Parsing.skipEmpty(src, i + n); - var binding = Binding.parse(src, i + n); - if (!binding.isSuccess()) return ParseRes.error(src.loc(i + n), "Expected a binding in for-in loop"); - n += binding.n; + var varKw = JavaScript.parseDeclarationType(src, i + n); + n += varKw.n; + n += Parsing.skipEmpty(src, i + n); + + var bindingLoc = src.loc(i + n); + + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return name.chainError(src.loc(i + n), "Expected a variable name"); + n += name.n; n += Parsing.skipEmpty(src, i + n); if (!Parsing.isIdentifier(src, i + n, "in")) return ParseRes.error(src.loc(i + n), "Expected 'in' keyword after variable declaration"); @@ -96,6 +106,6 @@ public class ForInNode extends Node { if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a for-in body"); n += bodyRes.n; - return ParseRes.res(new ForInNode(loc, label.result, binding.result, obj.result, bodyRes.result), n); + return ParseRes.res(new ForInNode(loc, label.result, new VariableNode(bindingLoc, name.result), varKw.isSuccess(), obj.result, bodyRes.result), n); } } 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 2b78601..8d82215 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -25,8 +25,6 @@ public class ForNode extends Node { } @Override public void compile(CompileResult target, boolean pollute) { var subtarget = target.subtarget(); - subtarget.scope.singleEntry = false; - subtarget.beginScope(); declaration.compile(subtarget, false, BreakpointType.STEP_OVER); @@ -38,20 +36,16 @@ public class ForNode extends Node { LabelContext.pushLoop(subtarget.env, loc(), label, end, start); CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(subtarget.env, label); - - subtarget.reallocScope(); CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); int endI = subtarget.size(); end.set(endI); + LabelContext.popLoop(subtarget.env, label); subtarget.add(Instruction.jmp(start - endI)); subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); if (pollute) subtarget.add(Instruction.pushUndefined()); - - subtarget.endScope(); } 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/SwitchNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java index 51cb2cc..db40293 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -43,41 +43,37 @@ public class SwitchNode extends Node { value.compile(target, true, BreakpointType.STEP_OVER); - var subtarget = target.subtarget(); - subtarget.beginScope(); - // TODO: create a jump map for (var ccase : cases) { - subtarget.add(Instruction.dup()); - ccase.value.compile(subtarget, true); - subtarget.add(Instruction.operation(Operation.EQUALS)); - caseToStatement.put(subtarget.temp(), ccase.statementI); + target.add(Instruction.dup()); + ccase.value.compile(target, true); + target.add(Instruction.operation(Operation.EQUALS)); + caseToStatement.put(target.temp(), ccase.statementI); } - int start = subtarget.temp(); + int start = target.temp(); var end = new DeferredIntSupplier(); LabelContext.getBreak(target.env).push(loc(), label, end); for (var stm : body) { - statementToIndex.put(statementToIndex.size(), subtarget.size()); - stm.compile(subtarget, false, BreakpointType.STEP_OVER); + statementToIndex.put(statementToIndex.size(), target.size()); + stm.compile(target, false, BreakpointType.STEP_OVER); } + + int endI = target.size(); + end.set(endI); LabelContext.getBreak(target.env).pop(label); - subtarget.endScope(); + target.add(Instruction.discard()); + if (pollute) target.add(Instruction.pushUndefined()); - int endI = subtarget.size(); - end.set(endI); - subtarget.add(Instruction.discard()); - if (pollute) subtarget.add(Instruction.pushUndefined()); - - if (defaultI < 0 || defaultI >= body.length) subtarget.set(start, Instruction.jmp(endI - start)); - else subtarget.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start)); + if (defaultI < 0 || defaultI >= body.length) target.set(start, Instruction.jmp(endI - start)); + else target.set(start, Instruction.jmp(statementToIndex.get(defaultI) - start)); for (var el : caseToStatement.entrySet()) { var i = statementToIndex.get(el.getValue()); if (i == null) i = endI; - subtarget.set(el.getKey(), Instruction.jmpIf(i - el.getKey())); + target.set(el.getKey(), Instruction.jmpIf(i - el.getKey())); } } 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 18700b3..f9e8846 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -12,7 +12,6 @@ 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,17 +41,11 @@ public class TryNode extends Node { catchStart = target.size() - start; if (captureName != null) { - var subtarget = target.subtarget(); - subtarget.beginScope(); - subtarget.scope.singleEntry = true; - - var catchVar = subtarget.scope.defineStrict(new Variable(captureName, false), catchBody.loc()); - subtarget.add(Instruction.loadError()); - subtarget.add(catchVar.index().toInit()); - catchBody.compile(subtarget, false); - subtarget.endScope(); - - subtarget.scope.end(); + var catchVar = target.scope.defineCatch(captureName); + target.add(Instruction.loadError()); + target.add(catchVar.index().toInit()); + catchBody.compile(target, false); + target.scope.undefineCatch(); } else catchBody.compile(target, false); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index d6d9447..49c0509 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -30,10 +30,10 @@ public class WhileNode extends Node { LabelContext.pushLoop(target.env, loc(), label, end, start); CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); - LabelContext.popLoop(target.env, label); var endI = target.size(); end.set(endI + 1); + LabelContext.popLoop(target.env, label); target.add(Instruction.jmp(start - end.getAsInt())); target.set(mid, Instruction.jmpIfNot(end.getAsInt() - mid + 1)); -- 2.45.2 From 54fe16393a34c49c89caffd39d59ac266d983eb9 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:10:11 +0200 Subject: [PATCH 23/41] regress: remove infrastructure for super references --- .../compilation/values/VariableNode.java | 88 +++----------- .../values/operations/CallNode.java | 115 ++---------------- .../values/operations/IndexNode.java | 17 +-- 3 files changed, 31 insertions(+), 189 deletions(-) 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 c4e90e4..ca32e06 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -1,9 +1,6 @@ package me.topchetoeu.jscript.compilation.values; -import java.util.function.IntFunction; - import me.topchetoeu.jscript.common.Instruction; -import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.Parsing; @@ -11,12 +8,9 @@ import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; -import me.topchetoeu.jscript.compilation.patterns.Pattern; -import me.topchetoeu.jscript.compilation.scope.Variable; -public class VariableNode extends Node implements Pattern, ChangeTarget { +public class VariableNode extends Node implements ChangeTarget { public final String name; public String assignName() { return name; } @@ -25,88 +19,38 @@ public class VariableNode extends Node implements Pattern, ChangeTarget { target.add(VariableNode.toGet(target, loc(), name)); } - @Override public void destructDeclResolve(CompileResult target) { - var i = target.scope.define(new Variable(name, false), loc()); - if (i != null) target.add(_i -> i.index().toUndefinedInit(false)); - } - @Override public void afterAssign(CompileResult target, boolean pollute) { target.add(VariableNode.toSet(target, loc(), name, pollute)); } - @Override public void declare(CompileResult target, DeclarationType decl, boolean lateInitializer) { - if (decl != null) { - var i = target.scope.define(decl, name, loc()); - target.add(_i -> i.index().toUndefinedInit(decl.strict)); - } - else target.add(_i -> { - var i = target.scope.get(name, false); - - if (i == null) return Instruction.globDef(name); - else return Instruction.nop(); - }); - - target.setLocation(loc()); - } - - @Override public void destruct(CompileResult target, DeclarationType decl, boolean shouldDeclare) { - if (!shouldDeclare || decl == null) { - if (shouldDeclare) target.add(VariableNode.toInit(target, loc(), name)); - else target.add(VariableNode.toInit(target, loc(), name)); - } - else { - if (decl == DeclarationType.VAR && target.scope.has(name, false)) throw new SyntaxException(loc(), "Duplicate parameter name not allowed"); - var v = target.scope.define(decl, name, loc()); - target.add(_i -> v.index().toInit()); - } - - target.setLocation(loc()); - } - @Override public void compile(CompileResult target, boolean pollute) { target.add(toGet(target, loc(), name, true, false)).setLocation(loc()); } - public static IntFunction toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) { - var oldI = target.scope.get(name, false); + public static Instruction toGet(CompileResult target, Location loc, String name, boolean keep, boolean forceGet) { + var i = target.scope.get(name, false); - if (oldI != null) { - if (keep) return _i -> oldI.index().toGet(); - else return _i -> Instruction.nop(); - } - else return _i -> { - var newI = target.scope.get(name, false); - - if (newI == null) return Instruction.globGet(name, forceGet); - else if (keep) return newI.index().toGet(); + if (i != null) { + if (keep) return i.index().toGet(); else return Instruction.nop(); - }; + } + else return Instruction.globGet(name, forceGet); } - public static IntFunction toGet(CompileResult target, Location loc, String name) { + public static Instruction toGet(CompileResult target, Location loc, String name) { return toGet(target, loc, name, true, false); } - public static IntFunction toInit(CompileResult target, Location loc, String name) { - var oldI = target.scope.get(name, false); + public static Instruction toInit(CompileResult target, Location loc, String name) { + var i = target.scope.get(name, false); - if (oldI != null) return _i -> oldI.index().toInit(); - else return _i -> { - var i = target.scope.get(name, false); - - if (i == null) return Instruction.globSet(name, false, true); - else return i.index().toInit(); - }; + if (i != null) return i.index().toInit(); + else return Instruction.globSet(name, false, true); } - public static IntFunction toSet(CompileResult target, Location loc, String name, boolean keep) { - var oldI = target.scope.get(name, false); + public static Instruction toSet(CompileResult target, Location loc, String name, boolean keep) { + var i = target.scope.get(name, false); - if (oldI != null) return _i -> oldI.index().toSet(keep); - else return _i -> { - var i = target.scope.get(name, false); - - if (i == null) return Instruction.globSet(name, keep, false); - else return i.index().toSet(keep); - }; + if (i != null) return i.index().toSet(keep); + else return Instruction.globSet(name, keep, false); } public VariableNode(Location loc, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index b225827..af2fbe2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -4,134 +4,40 @@ import java.util.ArrayList; import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; -import me.topchetoeu.jscript.common.json.JSON; -import me.topchetoeu.jscript.common.json.JSONElement; 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.ClassNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; -import me.topchetoeu.jscript.compilation.values.ArgumentsNode; -import me.topchetoeu.jscript.compilation.values.ArrayNode; -import me.topchetoeu.jscript.compilation.values.ObjectNode; -import me.topchetoeu.jscript.compilation.values.SuperNode; -import me.topchetoeu.jscript.compilation.values.ThisNode; -import me.topchetoeu.jscript.compilation.values.VariableNode; -import me.topchetoeu.jscript.compilation.values.constants.BoolNode; -import me.topchetoeu.jscript.compilation.values.constants.NumberNode; -import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class CallNode extends Node { - public static boolean ATTACH_NAME = true; - public final Node func; public final Node[] args; public final boolean isNew; - private String generateName(Node func, Node index) { - String res = "(intermediate value)"; - boolean shouldParen = false; - - if (func instanceof ObjectNode) { - var obj = (ObjectNode)func; - - shouldParen = true; - - if (obj.members.size() > 0) res = "{}"; - else res = "{(intermediate value)}"; - } - else if (func instanceof StringNode) { - res = JSON.stringify(JSONElement.string(((StringNode)func).value)); - } - else if (func instanceof NumberNode) { - res = JSON.stringify(JSONElement.number(((NumberNode)func).value)); - } - else if (func instanceof BoolNode) { - res = ((BoolNode)func).value ? "true" : "false"; - } - else if (func instanceof VariableNode) { - res = ((VariableNode)func).name; - } - else if (func instanceof ThisNode) { - res = "this"; - } - else if (func instanceof ArgumentsNode) { - res = "arguments"; - } - else if (func instanceof ArrayNode) { - var els = new ArrayList(); - - for (var el : ((ArrayNode)func).statements) { - if (el != null) els.add(generateName(el, null)); - else els.add("(intermediate value)"); - } - - res = "[" + String.join(",", els) + "]"; - } - - if (index == null) return res; - - if (shouldParen) res = "(" + res + ")"; - - if (index instanceof StringNode) { - var val = ((StringNode)index).value; - var bracket = JSON.stringify(JSONElement.string(val)); - - if (!bracket.substring(1, bracket.length() - 1).equals(val)) return res + "[" + bracket + "]"; - if (Parsing.parseIdentifier(new Source(val), 0).n != val.length()) return res + "[" + bracket + "]"; - - return res + "." + val; - } - - return res + "[" + generateName(index, null) + "]"; - } - @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - var superInstr = target.env.get(ClassNode.SUPER); + if (!isNew && func instanceof IndexNode indexNode) { + var obj = indexNode.object; + var index = indexNode.index; - if (!isNew && func instanceof SuperNode && superInstr != null && target.env.hasNotNull(ClassNode.ON_SUPER_CALL)) { - target.env.get(ClassNode.SUPER_CONSTR).accept(target); - for (var arg : args) arg.compile(target, true); - target.add(Instruction.callSuper(args.length)); - target.env.get(ClassNode.ON_SUPER_CALL).accept(target); - } - else if (!isNew && func instanceof IndexNode indexn) { - var obj = indexn.object; - var index = indexn.index; - String name = ""; - - if (superInstr != null && obj instanceof SuperNode) { - new ThisNode(null).compile(target, true); - target.env.get(ClassNode.SUPER).accept(target); - IndexNode.indexLoad(target, index, true); - } - else { - obj.compile(target, true); - target.add(Instruction.dup()); - IndexNode.indexLoad(target, index, true); - } + obj.compile(target, true); + target.add(Instruction.dup()); + IndexNode.indexLoad(target, index, true); for (var arg : args) arg.compile(target, true); - if (ATTACH_NAME) name = generateName(obj, index); - - target.add(Instruction.call(args.length, true, name)); + target.add(Instruction.call(args.length, true)); target.setLocationAndDebug(loc(), type); } else { - String name = ""; - func.compile(target, true); for (var arg : args) arg.compile(target, true); - if (ATTACH_NAME) name = generateName(func, null); - - if (isNew) target.add(Instruction.callNew(args.length, name)).setLocationAndDebug(loc(), type); - else target.add(Instruction.call(args.length, false, name)).setLocationAndDebug(loc(), type); + if (isNew) target.add(Instruction.callNew(args.length)).setLocationAndDebug(loc(), type); + else target.add(Instruction.call(args.length, false)).setLocationAndDebug(loc(), type); } if (!pollute) target.add(Instruction.discard()); } @@ -168,7 +74,8 @@ public class CallNode extends Node { prevArg = true; } else if (argRes.isError()) return argRes.chainError(); - else if (prevArg && src.is(i + n, ",")) { + + if (prevArg && src.is(i + n, ",")) { prevArg = false; n++; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index 752e9a9..ea2544e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -6,12 +6,10 @@ 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.ClassNode; import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; -import me.topchetoeu.jscript.compilation.values.SuperNode; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode; @@ -20,11 +18,11 @@ public class IndexNode extends Node implements ChangeTarget { public final Node index; @Override public void beforeAssign(CompileResult target) { - compileObj(target, object); + object.compile(target, true); indexStorePushKey(target, index); } @Override public void beforeChange(CompileResult target) { - compileObj(target, object); + object.compile(target, true); if (index instanceof NumberNode num && (int)num.value == num.value) { target.add(Instruction.dup()); @@ -44,7 +42,7 @@ public class IndexNode extends Node implements ChangeTarget { } @Override public void assign(CompileResult target, boolean pollute) { - compileObj(target, object); + object.compile(target, true); target.add(Instruction.dup(1, 1)); indexStorePushKey(target, index); indexStore(target, index, pollute); @@ -55,7 +53,7 @@ public class IndexNode extends Node implements ChangeTarget { } public void compile(CompileResult target, boolean dupObj, boolean pollute) { - compileObj(target, object); + object.compile(target, true); if (dupObj) target.add(Instruction.dup()); if (index instanceof NumberNode num && (int)num.value == num.value) { @@ -83,13 +81,6 @@ public class IndexNode extends Node implements ChangeTarget { this.index = index; } - private static void compileObj(CompileResult target, Node obj) { - if (obj instanceof SuperNode && target.env.hasNotNull(ClassNode.SUPER)) { - target.env.get(ClassNode.SUPER).accept(target); - } - else obj.compile(target, true); - } - public static ParseRes parseIndex(Source src, int i, Node prev, int precedence) { if (precedence > 18) return ParseRes.failed(); -- 2.45.2 From fe8f65faf54321ddd3ec9c0a66ff5c9900612497 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:10:47 +0200 Subject: [PATCH 24/41] some final stuff in parsing --- .../jscript/compilation/FunctionNode.java | 67 +++++++------------ .../compilation/FunctionStatementNode.java | 8 ++- .../compilation/FunctionValueNode.java | 9 ++- .../compilation/VariableDeclareNode.java | 50 +++++--------- .../compilation/values/ArgumentsNode.java | 2 +- 5 files changed, 54 insertions(+), 82 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index b4a53b1..38da50a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -1,5 +1,7 @@ package me.topchetoeu.jscript.compilation; +import java.util.List; + import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.environment.Environment; @@ -7,65 +9,49 @@ 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.JavaScript.DeclarationType; import me.topchetoeu.jscript.compilation.scope.FunctionScope; -import me.topchetoeu.jscript.compilation.scope.Variable; +import me.topchetoeu.jscript.compilation.values.VariableNode; public abstract class FunctionNode extends Node { public final CompoundNode body; - public final Parameters params; + public final List params; public final Location end; public abstract String name(); + public final String name(String fallback) { + return this.name() != null ? this.name() : fallback; + } protected final int[] captures(int id, CompileResult target) { - return ((FunctionScope)target.children.get(id).scope).getCaptureIndices(); + return target.children.get(id).scope.getCaptureIndices(); } - protected void compilePreBody(CompileResult target) { } - - protected Environment rootEnv(Environment env) { + protected final Environment rootEnv(Environment env) { return env.get(JavaScript.COMPILE_ROOT); } public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { - var name = this.name() != null ? this.name() : _name; + var target = new CompileResult(env, scope, params.size()); + var i = 0; - return new CompileResult(env, scope, params.params.size(), target -> { - compilePreBody(target); + for (var param : params) { + var index = scope.define(param.name); - if (params.params.size() > 0) { - target.add(Instruction.loadArgs(true)); - if (params.params.size() > 1) target.add(Instruction.dup(params.params.size() - 1, 0)); - var i = 0; + target.add(Instruction.loadArg(i++)); + target.add(index.index().toInit()); + } - for (var param : params.params) { - target.add(Instruction.loadMember(i++)); - param.destruct(target, DeclarationType.VAR, true); - } - } + // if (selfName != null && !scope.has(selfName, false)) { + // var i = scope.defineSpecial(new Variable(selfName, true), end); - if (params.rest != null) { - target.add(Instruction.loadRestArgs(params.params.size())); - params.rest.destruct(target, DeclarationType.VAR, true); - } + // target.add(Instruction.loadCalled()); + // target.add(_i -> i.index().toInit()); + // } - if (selfName != null && !scope.has(name, false)) { - var i = scope.defineSpecial(new Variable(selfName, true), end); + body.resolve(target); + body.compile(target, lastReturn, BreakpointType.NONE); - target.add(Instruction.loadCallee()); - target.add(_i -> i.index().toInit()); - } - - body.resolve(target); - body.compile(target, lastReturn, BreakpointType.NONE); - - scope.end(); - - for (var child : target.children) child.buildTask.run(); - - scope.finish(); - }); + return target; } public final CompileResult compileBody(CompileResult parent, String name, String selfName) { return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName); @@ -82,13 +68,12 @@ public abstract class FunctionNode extends Node { compile(target, pollute, (String)null, BreakpointType.NONE); } - public FunctionNode(Location loc, Location end, Parameters params, CompoundNode body) { + public FunctionNode(Location loc, Location end, List params, CompoundNode body) { super(loc); this.end = end; this.params = params; this.body = body; - this.body.hasScope = false; } public static void compileWithName(Node stm, CompileResult target, boolean pollute, String name) { @@ -112,7 +97,7 @@ public abstract class FunctionNode extends Node { n += name.n; n += Parsing.skipEmpty(src, i + n); - var params = Parameters.parseParameters(src, i + n); + var params = JavaScript.parseParameters(src, i + n); if (!params.isSuccess()) return params.chainError(src.loc(i + n), "Expected a parameter list"); n += params.n; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index abaee3e..698f3dd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -1,5 +1,7 @@ package me.topchetoeu.jscript.compilation; +import java.util.List; + import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.parsing.Location; @@ -12,17 +14,17 @@ public class FunctionStatementNode extends FunctionNode { @Override public String name() { return name; } @Override public void resolve(CompileResult target) { - target.scope.define(new Variable(name, false), end); + target.scope.define(new Variable(name, false)); } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, true, false, false, name, captures(id, target))); + target.add(Instruction.loadFunc(id, name, captures(id, target))); target.add(VariableNode.toInit(target, end, this.name)); if (pollute) target.add(Instruction.pushUndefined()); } - public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { + public FunctionStatementNode(Location loc, Location end, List params, CompoundNode body, String name) { super(loc, end, params, body); this.name = name; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index cf30f75..3d5794a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -1,8 +1,11 @@ package me.topchetoeu.jscript.compilation; +import java.util.List; + 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.values.VariableNode; public class FunctionValueNode extends FunctionNode { public final String name; @@ -10,11 +13,11 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, name, null)); - target.add(_i -> Instruction.loadFunc(id, true, true, false, false, name, captures(id, target))); + var id = target.addChild(compileBody(target, name, name)); + target.add(Instruction.loadFunc(id, name, captures(id, target))); } - public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) { + public FunctionValueNode(Location loc, Location end, List params, CompoundNode body, String name) { super(loc, end, params, body); this.name = name; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 85e3d0f..de32404 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -3,58 +3,40 @@ package me.topchetoeu.jscript.compilation; import java.util.ArrayList; import java.util.List; +import com.github.bsideup.jabel.Desugar; + import me.topchetoeu.jscript.common.Instruction; 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.JavaScript.DeclarationType; -import me.topchetoeu.jscript.compilation.patterns.Pattern; +import me.topchetoeu.jscript.compilation.values.VariableNode; public class VariableDeclareNode extends Node { - public static class Pair { - public final Pattern destructor; - public final Node value; - public final Location location; - - public Pair(Pattern destr, Node value, Location location) { - this.destructor = destr; - this.value = value; - this.location = location; - } - } + @Desugar + public static record Pair(VariableNode var, Node value) { } public final List values; - public final DeclarationType declType; @Override public void resolve(CompileResult target) { - if (!declType.strict) { - for (var entry : values) { - entry.destructor.destructDeclResolve(target); - } - } + for (var entry : values) { + target.scope.define(entry.var.name); + } } @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { - if (entry.value == null) { - if (declType == DeclarationType.VAR) entry.destructor.declare(target, null, false); - else entry.destructor.declare(target, declType, false); - } - else { + if (entry.value != null) { entry.value.compile(target, true); - - if (declType == DeclarationType.VAR) entry.destructor.destruct(target, null, true); - else entry.destructor.destruct(target, declType, true); + target.add(VariableNode.toInit(target, loc(), entry.var.name)); } } if (pollute) target.add(Instruction.pushUndefined()); } - public VariableDeclareNode(Location loc, DeclarationType declType, List values) { + public VariableDeclareNode(Location loc, List values) { super(loc); this.values = values; - this.declType = declType; } public static ParseRes parse(Source src, int i) { @@ -70,14 +52,14 @@ public class VariableDeclareNode extends Node { var end = JavaScript.parseStatementEnd(src, i + n); if (end.isSuccess()) { n += end.n; - return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n); + return ParseRes.res(new VariableDeclareNode(loc, res), n); } while (true) { var nameLoc = src.loc(i + n); - var name = Pattern.parse(src, i + n, false); - if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name or a destructor"); + var name = Parsing.parseIdentifier(src, i + n); + if (!name.isSuccess()) return name.chainError(nameLoc, "Expected a variable name"); n += name.n; Node val = null; @@ -96,7 +78,7 @@ public class VariableDeclareNode extends Node { val = valRes.result; } - res.add(new Pair(name.result, val, nameLoc)); + res.add(new Pair(new VariableNode(nameLoc, name.result), val)); if (src.is(i + n, ",")) { n++; @@ -107,7 +89,7 @@ public class VariableDeclareNode extends Node { if (end.isSuccess()) { n += end.n + endN - n; - return ParseRes.res(new VariableDeclareNode(loc, declType.result, res), n); + return ParseRes.res(new VariableDeclareNode(loc, res), n); } else return end.chainError(src.loc(i + n), "Expected a comma or end of statement"); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java index 38ab529..104340f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java @@ -8,7 +8,7 @@ import me.topchetoeu.jscript.compilation.Node; public class ArgumentsNode extends Node { @Override public void compile(CompileResult target, boolean pollute) { - if (pollute) target.add(Instruction.loadArgs(false)); + if (pollute) target.add(Instruction.loadArgs()); } public ArgumentsNode(Location loc) { -- 2.45.2 From 92fb0dbbfd1e913d0a728cc7391f3290be5e50c2 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:11:12 +0200 Subject: [PATCH 25/41] regress: simplify invoke model --- .../jscript/runtime/values/Value.java | 31 ++++++------------- 1 file changed, 9 insertions(+), 22 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index e70d05d..dc7f293 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -82,38 +82,25 @@ public abstract class Value { return this == NumberValue.NAN || this instanceof NumberValue num && Double.isNaN(num.getDouble()); } - public Value call(Environment env, boolean isNew, String name, Value self, Value ...args) { - if (name == null || name.equals("")) name = "(intermediate value)"; - - if (isNew) throw EngineException.ofType(name + " is not a constructor"); - else throw EngineException.ofType(name + " is not a function"); + public Value apply(Environment env, Value self, Value ...args) { + throw EngineException.ofType("Value is not a function"); } + public Value construct(Environment env, Value self, Value ...args) { + throw EngineException.ofType("Value is not a constructor"); + } - public final Value apply(Environment env, String name, Value self, Value ...args) { - return call(env, false, name, self, args); - } - public final Value construct(Environment env, String name, Value ...args) { + public final Value construct(Environment env, Value ...args) { var res = new ObjectValue(); var proto = getMember(env, StringValue.of("prototype")); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); - var ret = this.call(env, true, name, res, args); + var ret = this.construct(env, res, args); - if (!ret.isPrimitive()) return ret; - return res; - } - public final Value construct(Environment env, String name, Value self, Value ...args) { - var ret = this.call(env, true, name, self, args); - return ret.isPrimitive() ? self : ret; + if (ret == Value.UNDEFINED || ret.isPrimitive()) return res; + return ret; } - public final Value apply(Environment env, Value self, Value ...args) { - return apply(env, "", self, args); - } - public final Value construct(Environment env, Value ...args) { - return construct(env, "", args); - } public abstract Value toPrimitive(Environment env); public abstract NumberValue toNumber(Environment env); -- 2.45.2 From b4e7a429757f1b32ec6c0d54bbd583f986765264 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:11:57 +0200 Subject: [PATCH 26/41] regress: remove ES6 stuff (except apply and construct constraints) from funcs --- .../values/functions/CodeFunction.java | 10 ++------ .../values/functions/FunctionValue.java | 25 ++++++++----------- .../values/functions/NativeFunction.java | 2 +- 3 files changed, 14 insertions(+), 23 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index e72a38a..5183b62 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -10,9 +10,6 @@ public final class CodeFunction extends FunctionValue { public final Value[][] captures; public Environment env; - public Value self; - public Value argsVal; - private Value onCall(Frame frame) { frame.onPush(); @@ -27,11 +24,8 @@ public final class CodeFunction extends FunctionValue { } } - @Override public Value onCall(Environment env, boolean isNew, String name, Value thisArg, Value ...args) { - var frame = new Frame(env, isNew, thisArg, args, this); - if (argsVal != null) frame.fakeArgs = argsVal; - if (self != null) frame.self = self; - if (mustCallSuper && isNew) frame.self = null; + @Override public Value onCall(Environment env, boolean isNew, Value self, Value ...args) { + var frame = new Frame(env, isNew, self, args, this); var res = onCall(frame); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index b44c76b..3038536 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -7,7 +7,6 @@ import java.util.List; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.debug.DebugContext; -import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; @@ -21,9 +20,8 @@ public abstract class FunctionValue extends ObjectValue { public int length; public Value prototype = new ObjectValue(); - public boolean enableCall = true; - public boolean enableNew = true; - public boolean mustCallSuper = false; + public boolean enableApply = true; + public boolean enableConstruct = true; private final FieldMember nameField = new FieldMember(this, true, false, false) { @Override public Value get(Environment env, Value self) { @@ -53,18 +51,17 @@ public abstract class FunctionValue extends ObjectValue { } }; - protected abstract Value onCall(Environment ext, boolean isNew, String name, Value thisArg, Value ...args); + protected abstract Value onCall(Environment ext, boolean isNew, Value thisArg, Value ...args); @Override public String toString() { return String.format("function %s(...)", name); } - @Override public Value call(Environment ext, boolean isNew, String name, Value thisArg, Value ...args) { - if (isNew && !enableNew) super.call(ext, isNew, name, thisArg, args); - if (!isNew && !enableCall) { - if (name == null || name.equals("")) name = "(intermediate value)"; - throw EngineException.ofType(name + " is not invokable"); - } - - return onCall(ext, isNew, name, thisArg, args); - } + @Override public Value apply(Environment env, Value self, Value... args) { + if (!enableApply) throw new RuntimeException("Function cannot be applied"); + return onCall(env, false, self, args); + } + @Override public Value construct(Environment env, Value self, Value... args) { + if (!enableConstruct) throw new RuntimeException("Function cannot be constructed"); + return onCall(env, true, self, args); + } @Override public Member getOwnMember(Environment env, KeyCache key) { switch (key.toString(env)) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index 0ad250a..f2bdc47 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -10,7 +10,7 @@ public final class NativeFunction extends FunctionValue { public final NativeFunctionRunner action; - @Override public Value onCall(Environment env, boolean isNew, String name, Value self, Value ...args) { + @Override public Value onCall(Environment env, boolean isNew, Value self, Value ...args) { return action.run(new Arguments(env, isNew, self, args)); } -- 2.45.2 From 41bb27e4dde5aa71b43fb2a3846206d79c5588ea Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sat, 23 Nov 2024 20:15:42 +0200 Subject: [PATCH 27/41] implement all changes in runtime --- .../me/topchetoeu/jscript/runtime/Frame.java | 33 +++-- .../jscript/runtime/InstructionRunner.java | 140 +++++------------- .../jscript/runtime/SimpleRepl.java | 10 +- .../jscript/runtime/values/Member.java | 4 +- 4 files changed, 64 insertions(+), 123 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index 636a881..62ef129 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -1,5 +1,6 @@ package me.topchetoeu.jscript.runtime; +import java.util.Arrays; import java.util.Stack; import java.util.concurrent.CancellationException; @@ -12,6 +13,7 @@ import me.topchetoeu.jscript.runtime.values.Value; import me.topchetoeu.jscript.runtime.values.functions.CodeFunction; import me.topchetoeu.jscript.runtime.values.objects.ArrayLikeValue; import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.numbers.IntValue; public final class Frame { public static final Key KEY = Key.of(); @@ -95,17 +97,26 @@ public final class Frame { } /** - * A list of one-element arrays of values. This is so that we can pass captures to other functions + * An array of captures from the parent function */ public final Value[][] captures; + /** + * An array of non-capture variables + */ public final Value[] locals; + /** + * An array of children-captured variables + */ public final Value[][] capturables; - public final Value argsVal; - public Value self; - public Value fakeArgs; + + public final Value self; public final Value[] args; - public final boolean isNew; - public final Stack tryStack = new Stack<>(); + public final Value argsVal; + public final Value argsLen; + + public final boolean isNew; + + public final Stack tryStack = new Stack<>(); public final CodeFunction function; public final Environment env; private final DebugContext dbg; @@ -275,11 +286,8 @@ public final class Frame { } if (returnValue != null) { - if (self == null) error = EngineException.ofError("Super constructor must be called before returning"); - else { dbg.onInstruction(env, this, instr, returnValue, null, false); return returnValue; - } } if (error != null) { var caught = false; @@ -358,18 +366,21 @@ public final class Frame { }; } - public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) { + public Frame(Environment env, boolean isNew, Value self, Value[] args, CodeFunction func) { this.env = env; this.dbg = DebugContext.get(env); this.function = func; this.isNew = isNew; - this.self = thisArg; + this.self = self; this.args = args; this.argsVal = new ArgumentsValue(this, args); + this.argsLen = new IntValue(args.length); this.captures = func.captures; this.locals = new Value[func.body.localsN]; + Arrays.fill(this.locals, Value.UNDEFINED); this.capturables = new Value[func.body.capturablesN][1]; + for (var i = 0; i < this.capturables.length; i++) this.capturables[i][0] = Value.UNDEFINED; } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index db4ec83..cbb6857 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -34,23 +34,7 @@ public class InstructionRunner { var func = frame.pop(); var self = (boolean)instr.get(1) ? frame.pop() : Value.UNDEFINED; - frame.push(func.apply(env, instr.get(2), self, callArgs)); - - frame.codePtr++; - return null; - } - private static Value execCallSuper(Environment env, Instruction instr, Frame frame) { - if (!frame.isNew) throw EngineException.ofError("Super constructor may be called only when constructing"); - if (frame.self != null) throw EngineException.ofError("Super constructor may be called once"); - - var callArgs = frame.take(instr.get(0)); - var superFunc = frame.pop(); - - var self = new ObjectValue(); - if (frame.function.prototype instanceof ObjectValue objProto) self.setPrototype(env, objProto); - - frame.self = superFunc.construct(env, "super", self, callArgs); - frame.push(frame.self); + frame.push(func.apply(env, self, callArgs)); frame.codePtr++; return null; @@ -74,10 +58,10 @@ public class InstructionRunner { if (val == Value.UNDEFINED) accessor = null; else if (val instanceof FunctionValue func) accessor = func; - else throw EngineException.ofType("Getter must be a function or undefined."); + else throw EngineException.ofType("Getter must be a function or undefined"); - if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, instr.get(1))); - else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, instr.get(1))); + if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, true)); + else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, true)); frame.codePtr++; return null; @@ -87,7 +71,7 @@ public class InstructionRunner { var key = frame.pop(); var obj = frame.pop(); - obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, instr.get(0), true)); + obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, true, true)); frame.codePtr++; return null; @@ -155,9 +139,7 @@ public class InstructionRunner { private static Value execLoadVar(Environment env, Instruction instr, Frame frame) { int i = instr.get(0); - var res = frame.getVar(i); - if (res == null) throw EngineException.ofSyntax("Uninitialized variable"); - frame.push(res); + frame.push(frame.getVar(i)); frame.codePtr++; return null; @@ -189,25 +171,14 @@ public class InstructionRunner { private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) { int id = instr.get(0); String name = instr.get(1); - boolean callable = instr.get(2); - boolean constructible = instr.get(3); - boolean captureThis = instr.get(4); - boolean noThis = instr.get(5); - var captures = new Value[instr.params.length - 6][]; + var captures = new Value[instr.params.length - 2][]; - for (var i = 6; i < instr.params.length; i++) { - captures[i - 6] = frame.captureVar(instr.get(i)); + for (var i = 2; i < instr.params.length; i++) { + captures[i - 2] = frame.captureVar(instr.get(i)); } var func = new CodeFunction(env, name, frame.function.body.children[id], captures); - if (!callable) func.enableCall = false; - if (!constructible) func.enableNew = false; - if (captureThis) { - func.self = frame.self; - func.argsVal = frame.argsVal; - } - if (noThis) func.mustCallSuper = true; frame.push(func); @@ -254,7 +225,7 @@ public class InstructionRunner { frame.push(env.get(Value.REGEX_CONSTR).construct(env, instr.get(0), instr.get(1))); } else { - throw EngineException.ofSyntax("Regex is not supported."); + throw EngineException.ofSyntax("Regex is not supported"); } frame.codePtr++; @@ -271,7 +242,7 @@ public class InstructionRunner { var key = frame.pop(); var obj = frame.pop(); - if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'."); + if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'"); if ((boolean)instr.get(0)) frame.push(val); frame.codePtr++; return null; @@ -280,7 +251,7 @@ public class InstructionRunner { var val = frame.pop(); var obj = frame.pop(); - if (!obj.setMember(env, (String)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'."); + if (!obj.setMember(env, (String)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'"); if ((boolean)instr.get(1)) frame.push(val); frame.codePtr++; return null; @@ -289,7 +260,7 @@ public class InstructionRunner { var val = frame.pop(); var obj = frame.pop(); - if (!obj.setMember(env, (int)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'."); + if (!obj.setMember(env, (int)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'"); if ((boolean)instr.get(1)) frame.push(val); frame.codePtr++; return null; @@ -298,7 +269,6 @@ public class InstructionRunner { var val = (boolean)instr.get(1) ? frame.peek() : frame.pop(); int i = instr.get(0); - if (!(boolean)instr.get(2) && frame.getVar(i) == null) throw EngineException.ofSyntax("Uninitialized variable"); frame.setVar(i, val); frame.codePtr++; @@ -348,7 +318,7 @@ public class InstructionRunner { var key = frame.pop(); var val = frame.pop(); - if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'."); + if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'"); frame.codePtr++; return null; } @@ -456,7 +426,7 @@ public class InstructionRunner { return null; } - private static Value exexGlobDef(Environment env, Instruction instr, Frame frame) { + private static Value execGlobDef(Environment env, Instruction instr, Frame frame) { var name = (String)instr.get(0); if (!Value.global(env).hasMember(env, name, false)) { @@ -466,7 +436,7 @@ public class InstructionRunner { frame.codePtr++; return null; } - private static Value exexGlobGet(Environment env, Instruction instr, Frame frame) { + private static Value execGlobGet(Environment env, Instruction instr, Frame frame) { var name = (String)instr.get(0); if ((boolean)instr.get(1)) { frame.push(Value.global(env).getMember(env, name)); @@ -481,7 +451,7 @@ public class InstructionRunner { frame.codePtr++; return null; } - private static Value exexGlobSet(Environment env, Instruction instr, Frame frame) { + private static Value execGlobSet(Environment env, Instruction instr, Frame frame) { var name = (String)instr.get(0); var keep = (boolean)instr.get(1); var define = (boolean)instr.get(2); @@ -497,34 +467,19 @@ public class InstructionRunner { frame.codePtr++; return null; } - private static Value execExtend(Environment env, Instruction instr, Frame frame) { - var superVal = frame.peek(0); - var derivedVal = frame.peek(1); - - if (!(superVal instanceof FunctionValue superFunc)) throw EngineException.ofType("Illegal EXTENDS instruction"); - if (!(superFunc.prototype instanceof ObjectValue superProto)) throw EngineException.ofType("Illegal EXTENDS instruction"); - if (!(derivedVal instanceof FunctionValue derivedFunc)) throw EngineException.ofType("Illegal EXTENDS instruction"); - - derivedFunc.setPrototype(env, superFunc); - derivedFunc.prototype.setPrototype(env, superProto); + private static Value execLoadArg(Environment env, Instruction instr, Frame frame) { + frame.push(frame.args[(int)instr.get(0)]); + frame.codePtr++; + return null; + } + private static Value execLoadArgsN(Environment env, Instruction instr, Frame frame) { + frame.push(frame.argsLen); frame.codePtr++; return null; } - private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) { - if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal); - else frame.push(frame.fakeArgs); - frame.codePtr++; - return null; - } - private static Value execLoadRestArgs(Environment env, Instruction instr, Frame frame) { - int offset = instr.get(0); - var res = new ArrayValue(); - - if (offset < frame.args.length) res.copyFrom(frame.args, instr.get(0), 0, frame.args.length - offset); - - frame.push(res); + frame.push(frame.argsVal); frame.codePtr++; return null; } @@ -545,26 +500,7 @@ public class InstructionRunner { return null; } - private static Value execVarInit(Environment env, Instruction instr, Frame frame) { - if ((boolean)instr.get(1) || frame.getVar(instr.get(0)) == null) { - frame.setVar(instr.get(0), Value.UNDEFINED); - } - - frame.codePtr++; - return null; - } - private static Value execVarFree(Environment env, Instruction instr, Frame frame) { - frame.locals[(int)instr.get(0)] = null; - frame.codePtr++; - return null; - } - private static Value execCapFree(Environment env, Instruction instr, Frame frame) { - frame.capturables[(int)instr.get(0) - frame.locals.length] = new Value[1]; - frame.codePtr++; - return null; - } - - public static Value exec(Environment env, Instruction instr, Frame frame) { + public static Value exec(Environment env, Instruction instr, Frame frame) { switch (instr.type) { case NOP: return execNop(env, instr, frame); case RETURN: return execReturn(env, instr, frame); @@ -572,7 +508,6 @@ public class InstructionRunner { case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); case CALL: return execCall(env, instr, frame); case CALL_NEW: return execCallNew(env, instr, frame); - case CALL_SUPER: return execCallSuper(env, instr, frame); case TRY_START: return execTryStart(env, instr, frame); case TRY_END: return execTryEnd(env, instr, frame); @@ -593,12 +528,14 @@ public class InstructionRunner { case LOAD_REGEX: return execLoadRegEx(env, instr, frame); case LOAD_GLOB: return execLoadGlob(env, instr, frame); case LOAD_INTRINSICS: return execLoadIntrinsics(env, instr, frame); - case LOAD_ARGS: return execLoadArgs(env, instr, frame); - case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame); - case LOAD_CALLEE: return execLoadCallee(env, instr, frame); - case LOAD_THIS: return execLoadThis(env, instr, frame); case LOAD_ERROR: return execLoadError(env, instr, frame); + case LOAD_THIS: return execLoadThis(env, instr, frame); + case LOAD_ARG: return execLoadArg(env, instr, frame); + case LOAD_ARGS: return execLoadArgs(env, instr, frame); + case LOAD_ARGS_N: return execLoadArgsN(env, instr, frame); + case LOAD_CALLED: return execLoadCallee(env, instr, frame); + case DISCARD: return execDiscard(env, instr, frame); case STORE_MEMBER: return execStoreMember(env, instr, frame); case STORE_MEMBER_STR: return execStoreMemberStr(env, instr, frame); @@ -617,16 +554,11 @@ public class InstructionRunner { case OPERATION: return execOperation(env, instr, frame); - case GLOB_DEF: return exexGlobDef(env, instr, frame); - case GLOB_GET: return exexGlobGet(env, instr, frame); - case GLOB_SET: return exexGlobSet(env, instr, frame); - case EXTEND: return execExtend(env, instr, frame); + case GLOB_DEF: return execGlobDef(env, instr, frame); + case GLOB_GET: return execGlobGet(env, instr, frame); + case GLOB_SET: return execGlobSet(env, instr, frame); - case VAR_INIT: return execVarInit(env, instr, frame); - case VAR_FREE: return execVarFree(env, instr, frame); - case CAP_FREE: return execCapFree(env, instr, frame); - - default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "."); + default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ""); } } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index b6047dc..0e8bf90 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -212,12 +212,12 @@ public class SimpleRepl { res.defineOwnMember(env, "setCallable", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); - func.enableCall = args.get(1).toBoolean(); + func.enableApply = args.get(1).toBoolean(); return Value.UNDEFINED; })); res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); - func.enableNew = args.get(1).toBoolean(); + func.enableConstruct = args.get(1).toBoolean(); return Value.UNDEFINED; })); res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { @@ -229,16 +229,14 @@ public class SimpleRepl { var func = (FunctionValue)args.get(0); var self = args.get(1); var funcArgs = (ArrayValue)args.get(2); - var name = args.get(3).toString(env); - return func.apply(env, name, self, funcArgs.toArray()); + return func.apply(env, self, funcArgs.toArray()); })); res.defineOwnMember(env, "construct", new NativeFunction(args -> { var func = (FunctionValue)args.get(0); var funcArgs = (ArrayValue)args.get(1); - var name = args.get(2).toString(env); - return func.construct(env, name, funcArgs.toArray()); + return func.construct(env, funcArgs.toArray()); })); return res; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Member.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Member.java index 36c7ac1..9e3d964 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Member.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Member.java @@ -14,12 +14,12 @@ public interface Member { public boolean enumerable; @Override public Value get(Environment env, Value self) { - if (getter != null) return getter.call(env, false, "", self); + if (getter != null) return getter.apply(env, self); else return Value.UNDEFINED; } @Override public boolean set(Environment env, Value val, Value self) { if (setter == null) return false; - setter.call(env, false, "", self, val); + setter.apply(env, self, val); return true; } -- 2.45.2 From 61c5df5003d4503fa82cb2430f9f755b8666a46a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:47:15 +0200 Subject: [PATCH 28/41] fix: gd damn it --- .gitignore | 6 +- .../jscript/compilation/control/IfNode.java | 2 +- .../jscript/runtime/SimpleRepl.java | 68 +- src/main/resources/lib/index.js | 847 +++++++++--------- 4 files changed, 443 insertions(+), 480 deletions(-) diff --git a/.gitignore b/.gitignore index 9254aec..edf93b9 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,6 @@ * -!/src +!/src/ !/src/**/* !/doc @@ -21,4 +21,6 @@ !/gradle.properties !/gradle !/gradle/wrapper -!/gradle/wrapper/gradle-wrapper.properties \ No newline at end of file +!/gradle/wrapper/gradle-wrapper.properties + +!/ \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java index ef6546b..bd75010 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -80,7 +80,7 @@ public class IfNode extends Node { var a = JavaScript.parseExpression(src, i + n, 2); if (!a.isSuccess()) return a.chainError(src.loc(i + n), "Expected a value after the ternary operator."); n += a.n; - n += Parsing.skipEmpty(src, i); + n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, ":")) return ParseRes.failed(); n++; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index 0e8bf90..f36e833 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.Metadata; import me.topchetoeu.jscript.common.Reading; import me.topchetoeu.jscript.common.SyntaxException; import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.parsing.Filename; import me.topchetoeu.jscript.runtime.debug.DebugContext; @@ -252,21 +253,17 @@ public class SimpleRepl { res.defineOwnMember(env, "parse", new NativeFunction(args -> { return JSONConverter.toJs(JSON.parse(null, args.get(0).toString(env))); })); - res.defineOwnMember(env, "invokeType", new NativeFunction(args -> { - if (((ArgumentsValue)args.get(0)).frame.isNew) return StringValue.of("new"); - else return StringValue.of("call"); - })); - res.defineOwnMember(env, "invoke", new NativeFunction(args -> { - var func = (FunctionValue)args.get(0); - var self = args.get(1); - var funcArgs = (ArrayValue)args.get(2); - - return func.apply(env, self, funcArgs.toArray()); - })); return res; } + private static void setProto(Environment env, Environment target, Key key, ObjectValue repo, String name) { + var val = repo.getMember(env, name); + if (val instanceof ObjectValue obj) { + target.add(key, obj); + } + } + private static ObjectValue primordials(Environment env) { var res = new ObjectValue(); res.setPrototype(null, null); @@ -280,45 +277,20 @@ public class SimpleRepl { int[] i = new int[1]; - res.defineOwnMember(env, "setGlobalPrototype", new NativeFunction(args -> { - var type = args.get(0).toString(env); + res.defineOwnMember(env, "setGlobalPrototypes", new NativeFunction(args -> { var obj = (ObjectValue)args.get(1); - switch (type) { - case "object": - args.env.add(Value.OBJECT_PROTO, obj); - break; - case "function": - args.env.add(Value.FUNCTION_PROTO, obj); - break; - case "array": - args.env.add(Value.ARRAY_PROTO, obj); - break; - case "boolean": - args.env.add(Value.BOOL_PROTO, obj); - break; - case "number": - args.env.add(Value.NUMBER_PROTO, obj); - break; - case "string": - args.env.add(Value.STRING_PROTO, obj); - break; - case "symbol": - args.env.add(Value.SYMBOL_PROTO, obj); - break; - case "error": - args.env.add(Value.ERROR_PROTO, obj); - break; - case "syntax": - args.env.add(Value.SYNTAX_ERR_PROTO, obj); - break; - case "type": - args.env.add(Value.TYPE_ERR_PROTO, obj); - break; - case "range": - args.env.add(Value.RANGE_ERR_PROTO, obj); - break; - } + setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); + setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function"); + setProto(args.env, env, Value.ARRAY_PROTO, obj, "array"); + setProto(args.env, env, Value.BOOL_PROTO, obj, "boolean"); + setProto(args.env, env, Value.NUMBER_PROTO, obj, "number"); + setProto(args.env, env, Value.STRING_PROTO, obj, "string"); + setProto(args.env, env, Value.SYMBOL_PROTO, obj, "symbol"); + setProto(args.env, env, Value.ERROR_PROTO, obj, "error"); + setProto(args.env, env, Value.SYNTAX_ERR_PROTO, obj, "syntax"); + setProto(args.env, env, Value.TYPE_ERR_PROTO, obj, "type"); + setProto(args.env, env, Value.RANGE_ERR_PROTO, obj, "range"); return Value.UNDEFINED; })); diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index 023a569..cbdd81f 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -1,429 +1,418 @@ -const target = arguments[0]; -const primordials = arguments[1]; - -const symbol = primordials.symbol || (() => { - const repo = {}; - - return { - makeSymbol: (name) => { name }, - getSymbol(name) { - if (name in repo) return repo[name]; - else return repo[name] = { name }; - }, - getSymbolKey(symbol) { - if (symbol.name in repo && repo[symbol.name] === symbol) return symbol.name; - else return undefined; - }, - getSymbolDescription: ({ name }) => name, - }; -}); - -const number = primordials.number || { - parseInt() { throw new Error("parseInt not supported"); }, - parseFloat() { throw new Error("parseFloat not supported"); }, - isNaN: (val) => val !== val, - NaN: 0 / 0, - Infinity: 1 / 0, -}; - -const string = primordials.string; - -const object = primordials.object || { - defineProperty() { throw new Error("Define property not polyfillable"); }, - defineField(obj, key, a, b, c, value) { obj[key] = value; }, - getOwnMember() { throw new Error("Get own member not polyfillable"); }, - getOwnSymbolMember() { throw new Error("Get own symbol member not polyfillable"); }, - getOwnMembers() { throw new Error("Get own members not polyfillable"); }, - getOwnSymbolMembers() { throw new Error("Get own symbol members not polyfillable"); }, - getPrototype() { throw new Error("Get prototype not polyfillable"); }, - setPrototype() { throw new Error("Set prototype not polyfillable"); }, -} - -const invokeType = primordials.function.invokeType; -const setConstructable = primordials.function.setConstructable; -const setCallable = primordials.function.setCallable; -const invoke = primordials.function.invoke; -const construct = primordials.function.construct; - -const json = primordials.json; - -const setGlobalPrototype = primordials.setGlobalPrototype; -const compile = primordials.compile; -const setIntrinsic = primordials.setIntrinsic; - -const valueKey = symbol.makeSymbol("Primitive.value"); -const undefined = ({}).definitelyDefined; - -target.undefined = undefined; - -const unwrapThis = (self, type, constr, name, arg = "this", defaultVal) => { - if (typeof self === type) return self; - if (self instanceof constr && valueKey in self) self = self[valueKey]; - if (typeof self === type) return self; - if (defaultVal !== undefined) return defaultVal; - - throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); -} - -const wrapIndex = (i, len) => {}; - -class Symbol { - get description() { - return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); - } - toString() { - return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; - } - valueOf() { - return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); - } - - constructor(name = "") { - return symbol.makeSymbol(name); - } - - static for(name) { - return symbol.getSymbol(name + ""); - } - static keyFor(value) { - return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor")); - } -} - -setCallable(Symbol, true); -setConstructable(Symbol, false); - -object.defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); -object.defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); -object.defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); -object.defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); -object.defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); -object.defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); -object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); -object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); - -Symbol(); -target.Symbol = Symbol; - -class Number { - toString() { - return "" + unwrapThis(this, "number", Number, "Number.prototype.toString"); - } - valueOf() { - return unwrapThis(this, "number", Number, "Number.prototype.toString"); - } - - constructor(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return 0; - else return +value; - } - - this[valueKey] = target.Number(value); - } - - static isFinite(value) { - value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined); - - if (value === undefined || value !== value) return false; - if (value === Infinity || value === -Infinity) return false; - - return true; - } - static isInteger(value) { - value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined); - if (value === undefined) return false; - return number.parseInt(value) === value; - } - static isNaN(value) { - return number.isNaN(value); - } - static isSafeInteger(value) { - value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined); - if (value === undefined || number.parseInt(value) !== value) return false; - return value >= -9007199254740991 && value <= 9007199254740991; - } - static parseFloat(value) { - value = 0 + value; - return number.parseFloat(value); - } - static parseInt(value, radix) { - value = 0 + value; - radix = +radix; - if (number.isNaN(radix)) radix = 10; - - return number.parseInt(value, radix); - } -} - -object.defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); -object.defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); -object.defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); -object.defineField(Number, "POSITIVE_INFINITY", false, false, false, +number.Infinity); -object.defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity); -object.defineField(Number, "NaN", false, false, false, number.NaN); -object.defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); -object.defineField(Number, "MIN_VALUE", false, false, false, 5e-324); - -setCallable(Number, true); -target.Number = Number; -target.parseInt = Number.parseInt; -target.parseFloat = Number.parseFloat; -target.NaN = Number.NaN; -target.Infinity = Number.POSITIVE_INFINITY; - -class String { - at(index) { - throw "Not implemented :/"; - return unwrapThis(this, "string", String, "String.prototype.at")[index]; - } - toString() { - return unwrapThis(this, "string", String, "String.prototype.toString"); - } - valueOf() { - return unwrapThis(this, "string", String, "String.prototype.valueOf"); - } - - constructor(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return ""; - else return value + ""; - } - - this[valueKey] = String(value); - } - - static fromCharCode() { - const res = []; - res[arguments.length] = 0; - - for (let i = 0; i < arguments.length; i++) { - res[i] = string.fromCharCode(+arguments[i]); - } - - return string.stringBuild(res); - } - static fromCodePoint() { - const res = []; - res[arguments.length] = 0; - - for (let i = 0; i < arguments.length; i++) { - res[i] = string.fromCodePoint(+arguments[i]); - } - - return string.stringBuild(res); - } -} - -setCallable(String, true); -target.String = String; - -class Boolean { - toString() { - return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString"); - } - valueOf() { - return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf"); - } - - constructor(value) { - if (invokeType(arguments) === "call") { - if (arguments.length === 0) return false; - else return !!value; - } - - this[valueKey] = Boolean(value); - } -} - -setCallable(Boolean, true); -target.Boolean = Boolean; - -class Object { - toString() { - if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; - else if (typeof this === "number" || this instanceof Number) return "[object Number]"; - else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; - else if (typeof this === "string" || this instanceof String) return "[object String]"; - else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; - else if (typeof this === "function") return "[object Function]"; - else return "[object Object]"; - } - valueOf() { - return this; - } - - constructor(value) { - if (typeof value === 'object' && value !== null) return value; - if (typeof value === 'string') return new String(value); - if (typeof value === 'number') return new Number(value); - if (typeof value === 'boolean') return new Boolean(value); - if (typeof value === 'symbol') { - const res = {}; - setPrototype(res, Symbol.prototype); - res[valueKey] = value; - return res; - } - - return {}; - // // TODO: use new.target.prototype as proto - // if (target == null || typeof target !== 'object') target = {}; - - // return target; - } - - static defineProperty(obj, key, desc) { - if (typeof obj !== "object" || obj === null) throw new TypeError("Object.defineProperty called on non-object"); - if (typeof desc !== "object" || desc === null) throw new TypeError("Property description must be an object: " + desc); - - if ("get" in desc || "set" in desc) { - let get = desc.get, set = desc.set; - - if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); - if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); - - if ("value" in desc || "writable" in desc) { - throw new TypeError("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); - } - - if (!object.defineProperty(obj, key, desc.enumerable, desc.configurable, get, set)) { - throw new TypeError("Cannot redefine property: " + key); - } - } - else if (!object.defineField(obj, key, desc.writable, desc.enumerable, desc.configurable, desc.value)) { - throw new TypeError("Cannot redefine property: " + key); - } - - return obj; - } -} - -setCallable(Object, true); -object.setPrototype(Object.prototype, null); -target.Object = Object; - -class Function { - toString() { - if (this.name !== "") return "function " + this.name + "(...) { ... }"; - else return "function (...) { ... }"; - } - - constructor() { - const parts = ["(function annonymous("]; - - for (let i = 0; i < arguments.length - 1; i++) { - if (i > 0) parts[parts.length] = ","; - parts[parts.length] = arguments[i]; - } - parts[parts.length] = "){\n"; - parts[parts.length] = String(arguments[arguments.length - 1]); - parts[parts.length] = "\n})"; - - const res = compile(string.stringBuild(parts))(); - return res; - } - - static compile(src = "", { globals = [], wrap = false } = {}) { - const parts = []; - - if (wrap) parts[parts.length] = "return (function() {\n"; - if (globals.length > 0) { - parts[parts.length] = "let {"; - - for (let i = 0; i < globals.length; i++) { - if (i > 0) parts[parts.length] = ","; - parts[parts.length] = globals[i]; - } - - parts[parts.length] = "} = arguments[0];"; - } - - parts[parts.length] = src; - if (wrap) parts[parts.length] = "\n})(arguments[0])"; - - const res = compile(string.stringBuild(parts)); - return res; - } -} - -setCallable(Function, true); -target.Function = Function; - -class Array { - constructor(len) { - if (arguments.length === 1 && typeof len === "number") { - const res = []; - res.length = len; - return res; - } - // TODO: Implement spreading - else throw new Error("Spreading not implemented"); - } -} - -setCallable(Array, true); -target.Array = Array; - -class Error { - toString() { - let res = this.name || "Error"; - - const msg = this.message; - if (msg) res += ": " + msg; - - return res; - } - - constructor (msg = "") { - if (invokeType(arguments) === "call") return new Error(msg); - this.message = msg + ""; - } -} - -object.defineField(Error.prototype, "name", true, false, true, "Error"); -object.defineField(Error.prototype, "message", true, false, true, ""); -setCallable(Error, true); -target.Error = Error; - -class SyntaxError extends Error { - constructor (msg) { - if (invokeType(arguments) === "call") return new SyntaxError(msg); - super(msg); - } -} - -object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); -setCallable(SyntaxError, true); -target.SyntaxError = SyntaxError; - -class TypeError extends Error { - constructor (msg) { - if (invokeType(arguments) === "call") return new TypeError(msg); - super(msg); - } -} - -object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); -setCallable(TypeError, true); -target.TypeError = TypeError; - -class RangeError extends Error { - constructor (msg) { - if (invokeType(arguments) === "call") return new RangeError(msg); - super(msg); - } -} - -object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); -setCallable(RangeError, true); -target.RangeError = RangeError; - -setGlobalPrototype("string", String.prototype); -setGlobalPrototype("number", Number.prototype); -setGlobalPrototype("boolean", Boolean.prototype); -setGlobalPrototype("symbol", Symbol.prototype); -setGlobalPrototype("object", Object.prototype); -setGlobalPrototype("array", Array.prototype); -setGlobalPrototype("function", Function.prototype); -setGlobalPrototype("error", Error.prototype); -setGlobalPrototype("syntax", SyntaxError.prototype); +return; +(function main() { + var __extends = (this && this.__extends) || (function () { + var extendStatics = function (d, b) { + extendStatics = Object.setPrototypeOf || + ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || + function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; + return extendStatics(d, b); + }; + return function (d, b) { + if (typeof b !== "function" && b !== null) + throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); + extendStatics(d, b); + function __() { this.constructor = d; } + d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); + }; + })(); + + var target = arguments[0]; + var primordials = arguments[1]; + var symbol = primordials.symbol || (function () { + var repo = {}; + return { + makeSymbol: function (name) { return { name: name }; }, + getSymbol: function (name) { + if (name in repo) return repo[name]; + else return repo[name] = { name: name }; + }, + getSymbolKey: function (symbol) { + if (symbol.name in repo && repo[symbol.name] === symbol) return symbol.name; + else return undefined; + }, + getSymbolDescription: function (symbol) { + return symbol.name; + } + }; + }); + var number = primordials.number || { + parseInt: function () { throw new Error("parseInt not supported"); }, + parseFloat: function () { throw new Error("parseFloat not supported"); }, + isNaN: function (val) { return val !== val; }, + NaN: 0 / 0, + Infinity: 1 / 0, + }; + var string = primordials.string; + var object = primordials.object || { + defineProperty: function () { throw new Error("Define property not polyfillable"); }, + defineField: function (obj, key, a, b, c, value) { obj[key] = value; }, + getOwnMember: function () { throw new Error("Get own member not polyfillable"); }, + getOwnSymbolMember: function () { throw new Error("Get own symbol member not polyfillable"); }, + getOwnMembers: function () { throw new Error("Get own members not polyfillable"); }, + getOwnSymbolMembers: function () { throw new Error("Get own symbol members not polyfillable"); }, + getPrototype: function () { throw new Error("Get prototype not polyfillable"); }, + setPrototype: function () { throw new Error("Set prototype not polyfillable"); }, + }; + var func = primordials.function || { + invokeType: function (args, self) { + if (typeof self === "object") return "new"; + else return "call"; + }, + setConstructable: function () { throw new Error("Set constructable not polyfillable"); }, + setCallable: function () { throw new Error("Set callable not polyfillable"); }, + invoke: function () { throw new Error("Invoke not polyfillable"); }, + construct: function () { throw new Error("Construct not polyfillable"); }, + }; + var json = primordials.json || { + stringify: function (val) { throw new Error("JSON stringify not polyfillable"); }, + parse: function (val) { throw new Error("JSON parse not polyfillable"); }, + } + + var setGlobalPrototypes = primordials.setGlobalPrototype; + var compile = primordials.compile; + var setIntrinsic = primordials.setIntrinsic; + var valueKey = symbol.makeSymbol("Primitive.value"); + var undefined = void 0; + target.undefined = undefined; + + function unwrapThis(self, type, constr, name, arg, defaultVal) { + if (arg === void 0) { arg = "this"; } + if (typeof self === type) + return self; + if (self instanceof constr && valueKey in self) + self = self[valueKey]; + if (typeof self === type) + return self; + if (defaultVal !== undefined) + return defaultVal; + throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); + } + function wrapIndex(i, len) { } + var Symbol = /** @class */ (function () { + function Symbol(name) { + if (name === undefined) name = ""; + return symbol.makeSymbol(name); + } + Symbol.prototype.toString = function () { + return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; + }; + Symbol.prototype.valueOf = function () { + return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); + }; + Symbol.for = function (name) { + return symbol.getSymbol(name + ""); + }; + Symbol.keyFor = function (value) { + return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor")); + }; + return Symbol; + }()); + ; + object.defineProperty(Symbol.prototype, "desc", false, true, function () { + return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); + }); + object.defineField(Symbol, "asyncIterator", false, false, false, Symbol("Symbol.asyncIterator")); + object.defineField(Symbol, "iterator", false, false, false, Symbol("Symbol.iterator")); + object.defineField(Symbol, "match", false, false, false, Symbol("Symbol.match")); + object.defineField(Symbol, "matchAll", false, false, false, Symbol("Symbol.matchAll")); + object.defineField(Symbol, "replace", false, false, false, Symbol("Symbol.replace")); + object.defineField(Symbol, "search", false, false, false, Symbol("Symbol.search")); + object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); + object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); + func.setConstructable(Symbol, false); + + function Number(value) { + if (func.invokeType(arguments, this) === "call") { + if (arguments.length === 0) return 0; + else return +value; + } + this[valueKey] = target.Number(value); + } + Number.prototype.toString = function () { + return "" + unwrapThis(this, "number", Number, "Number.prototype.toString"); + }; + Number.prototype.valueOf = function () { + return unwrapThis(this, "number", Number, "Number.prototype.toString"); + }; + Number.isFinite = function (value) { + value = unwrapThis(value, "number", Number, "Number.isFinite", "value", undefined); + if (value === undefined || value !== value) + return false; + if (value === Infinity || value === -Infinity) + return false; + return true; + }; + Number.isInteger = function (value) { + value = unwrapThis(value, "number", Number, "Number.isInteger", "value", undefined); + if (value === undefined) + return false; + return number.parseInt(value) === value; + }; + Number.isNaN = function (value) { + return number.isNaN(value); + }; + Number.isSafeInteger = function (value) { + value = unwrapThis(value, "number", Number, "Number.isSafeInteger", "value", undefined); + if (value === undefined || number.parseInt(value) !== value) + return false; + return value >= -9007199254740991 && value <= 9007199254740991; + }; + Number.parseFloat = function (value) { + value = 0 + value; + return number.parseFloat(value); + }; + Number.parseInt = function (value, radix) { + value = 0 + value; + radix = +radix; + if (number.isNaN(radix)) + radix = 10; + return number.parseInt(value, radix); + }; + + object.defineField(Number, "EPSILON", false, false, false, 2.220446049250313e-16); + object.defineField(Number, "MIN_SAFE_INTEGER", false, false, false, -9007199254740991); + object.defineField(Number, "MAX_SAFE_INTEGER", false, false, false, 9007199254740991); + object.defineField(Number, "POSITIVE_INFINITY", false, false, false, +number.Infinity); + object.defineField(Number, "NEGATIVE_INFINITY", false, false, false, -number.Infinity); + object.defineField(Number, "NaN", false, false, false, number.NaN); + object.defineField(Number, "MAX_VALUE", false, false, false, 1.7976931348623157e+308); + object.defineField(Number, "MIN_VALUE", false, false, false, 5e-324); + func.setCallable(Number, true); + target.Number = Number; + target.parseInt = Number.parseInt; + target.parseFloat = Number.parseFloat; + target.NaN = Number.NaN; + target.Infinity = Number.POSITIVE_INFINITY; + + function String(value) { + if (func.invokeType(arguments, this) === "call") { + if (arguments.length === 0) + return ""; + else + return value + ""; + } + this[valueKey] = String(value); + } + String.prototype.at = function (index) { + throw "Not implemented :/"; + return unwrapThis(this, "string", String, "String.prototype.at")[index]; + }; + String.prototype.toString = function () { + return unwrapThis(this, "string", String, "String.prototype.toString"); + }; + String.prototype.valueOf = function () { + return unwrapThis(this, "string", String, "String.prototype.valueOf"); + }; + String.fromCharCode = function () { + var res = []; + res[arguments.length] = 0; + for (var i = 0; i < arguments.length; i++) { + res[i] = string.fromCharCode(+arguments[i]); + } + return string.stringBuild(res); + }; + String.fromCodePoint = function () { + var res = []; + res[arguments.length] = 0; + + for (var i = 0; i < arguments.length; i++) { + res[i] = string.fromCodePoint(+arguments[i]); + } + return string.stringBuild(res); + }; + func.setCallable(String, true); + target.String = String; + + function Boolean(value) { + if (func.invokeType(arguments, this) === "call") { + if (arguments.length === 0) return false; + else return !!value; + } + this[valueKey] = Boolean(value); + } + Boolean.prototype.toString = function () { + return "" + unwrapThis(this, "boolean", Boolean, "Boolean.prototype.toString"); + }; + Boolean.prototype.valueOf = function () { + return unwrapThis(this, "boolean", Boolean, "Boolean.prototype.valueOf"); + }; + func.setCallable(Boolean, true); + target.Boolean = Boolean; + + function Object(value) { + if (typeof value === 'object' && value !== null) return value; + if (typeof value === 'string') return new String(value); + if (typeof value === 'number') return new Number(value); + if (typeof value === 'boolean') return new Boolean(value); + if (typeof value === 'symbol') { + var res = {}; + setPrototype(res, Symbol.prototype); + res[valueKey] = value; + return res; + } + + return {}; + // // TODO: use new.target.prototype as proto + // if (target == null || typeof target !== 'object') target = {}; + // return target; + } + Object.prototype.toString = function () { + if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; + else if (typeof this === "number" || this instanceof Number) return "[object Number]"; + else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; + else if (typeof this === "string" || this instanceof String) return "[object String]"; + else if (typeof this === "boolean" || this instanceof Boolean) return "[object Boolean]"; + else if (typeof this === "function") return "[object Function]"; + else return "[object Object]"; + }; + Object.prototype.valueOf = function () { + return this; + }; + Object.defineProperty = function (obj, key, desc) { + if (typeof obj !== "object" || obj === null) throw new TypeError("Object.defineProperty called on non-object"); + if (typeof desc !== "object" || desc === null) throw new TypeError("Property description must be an object: " + desc); + if ("get" in desc || "set" in desc) { + var get = desc.get, set = desc.set; + + if (get !== undefined && typeof get !== "function") throw new TypeError("Getter must be a function: " + get); + if (set !== undefined && typeof set !== "function") throw new TypeError("Setter must be a function: " + set); + if ("value" in desc || "writable" in desc) { + throw new TypeError("Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); + } + if (!object.defineProperty(obj, key, desc.enumerable, desc.configurable, get, set)) { + throw new TypeError("Cannot redefine property: " + key); + } + } + else if (!object.defineField(obj, key, desc.writable, desc.enumerable, desc.configurable, desc.value)) { + throw new TypeError("Cannot redefine property: " + key); + } + + return obj; + }; + func.setCallable(Object, true); + object.setPrototype(Object.prototype, null); + target.Object = Object; + + function Function() { + var parts = ["(function annonymous("]; + for (var i = 0; i < arguments.length - 1; i++) { + if (i > 0) + parts[parts.length] = ","; + parts[parts.length] = arguments[i]; + } + parts[parts.length] = "){\n"; + parts[parts.length] = String(arguments[arguments.length - 1]); + parts[parts.length] = "\n})"; + var res = compile(string.stringBuild(parts))(); + return res; + } + Function.prototype.toString = function () { + if (this.name !== "") + return "function " + this.name + "(...) { ... }"; + else + return "function (...) { ... }"; + }; + Function.compile = function (src, opts) { + if (src === void 0) src = ""; + if (opts === void 0) opts = {}; + if (opts.globals === void 0) opts.globals = []; + if (opts.wrap === void 0) opts.wrap = false; + + var globals = opts.globals; + var wrap = opts.wrap; + var parts = []; + + if (wrap) parts[parts.length] = "return (function() {\n"; + if (globals.length > 0) { + parts[parts.length] = "let {"; + for (var i = 0; i < globals.length; i++) { + if (i > 0) parts[parts.length] = ","; + parts[parts.length] = globals[i]; + } + parts[parts.length] = "} = arguments[0];"; + } + parts[parts.length] = src; + if (wrap) parts[parts.length] = "\n})(arguments[0])"; + + var res = compile(string.stringBuild(parts)); + return res; + }; + func.setCallable(Function, true); + target.Function = Function; + + function Array(len) { + if (arguments.length === 1 && typeof len === "number") { + var res = []; + res.length = len; + return res; + } + // TODO: Implement spreading + else throw new Error("Spreading not implemented"); + } + func.setCallable(Array, true); + target.Array = Array; + + function Error(msg) { + if (msg === void 0) { msg = ""; } + if (func.invokeType(arguments, this) === "call") + return new Error(msg); + this.message = msg + ""; + } + Error.prototype.toString = function () { + var res = this.name || "Error"; + var msg = this.message; + if (msg) + res += ": " + msg; + return res; + }; + object.defineField(Error.prototype, "name", true, false, true, "Error"); + object.defineField(Error.prototype, "message", true, false, true, ""); + func.setCallable(Error, true); + target.Error = Error; + + __extends(SyntaxError, Error); + function SyntaxError(msg) { + if (func.invokeType(arguments, this) === "call") + return new SyntaxError(msg); + return _super.call(this, msg) || this; + } + object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); + func.setCallable(SyntaxError, true); + target.SyntaxError = SyntaxError; + + __extends(TypeError, Error); + function TypeError(msg) { + if (func.invokeType(arguments, this) === "call") + return new TypeError(msg); + return _super.call(this, msg) || this; + } + object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); + func.setCallable(TypeError, true); + target.TypeError = TypeError; + + __extends(RangeError, Error); + function RangeError(msg) { + if (func.invokeType(arguments, this) === "call") + return new RangeError(msg); + return _super.call(this, msg) || this; + } + object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); + func.setCallable(RangeError, true); + target.RangeError = RangeError; + + target.uint8 = primordials.uint8; + + setGlobalPrototypes({ + string: String.prototype, + number: Number.prototype, + boolean: Boolean.prototype, + symbol: Symbol.prototype, + object: Object.prototype, + array: Array.prototype, + function: Function.prototype, + error: Error.prototype, + syntax: SyntaxError.prototype, + range: RangeError.prototype, + type: TypeError.prototype, + }); +})(arguments[0], arguments[1]); -- 2.45.2 From 7f6df49fc54cccbbff11dd745f3251e5a283c285 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:47:51 +0200 Subject: [PATCH 29/41] fix: scope issues --- .../jscript/compilation/scope/FunctionScope.java | 14 +++++++------- .../jscript/compilation/scope/VariableIndex.java | 8 -------- .../jscript/compilation/scope/VariableList.java | 8 ++++++-- 3 files changed, 13 insertions(+), 17 deletions(-) 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 8d38d50..213f020 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/FunctionScope.java @@ -5,8 +5,7 @@ import java.util.HashMap; public final class FunctionScope { protected final VariableList locals = new VariableList(VariableIndex.IndexType.LOCALS); - protected final VariableList catches = new VariableList(VariableIndex.IndexType.LOCALS, this.locals); - protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this.catches); + protected final VariableList capturables = new VariableList(VariableIndex.IndexType.CAPTURABLES, this.locals); private final VariableList captures = new VariableList(VariableIndex.IndexType.CAPTURES); private final HashMap localsMap = new HashMap<>(); @@ -38,8 +37,10 @@ public final class FunctionScope { public Variable define(Variable var) { if (passthrough) return null; else { - var old = get(var.name, false); - if (old != null) return old; + var catchVar = getCatchVar(var.name); + if (catchVar != null) return catchVar; + if (localsMap.containsKey(var.name)) return localsMap.get(var.name); + if (capturesMap.containsKey(var.name)) throw new RuntimeException("HEY!"); localsMap.put(var.name, var); return locals.add(var); @@ -58,7 +59,7 @@ public final class FunctionScope { */ public Variable defineCatch(String name) { var var = new Variable(name, false); - this.catches.add(var); + this.locals.add(var); this.catchesMap.add(var); return var; } @@ -87,7 +88,7 @@ public final class FunctionScope { var parentVar = parent.get(name, true); if (parentVar == null) return null; - var childVar = captures.add(parentVar.clone()); + var childVar = captures.add(parentVar.clone().setIndexSupplier(null)); capturesMap.put(childVar.name, childVar); childToParent.put(childVar, parentVar); parentToChild.put(parentVar, childVar); @@ -103,7 +104,6 @@ public final class FunctionScope { * @param capture Whether or not to execute this capturing logic */ public Variable get(Variable var, boolean capture) { - if (catches.has(var)) return addCaptured(var, capture); if (captures.has(var)) return addCaptured(var, capture); if (locals.has(var)) return addCaptured(var, capture); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java index 278ab88..ea01ac9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableIndex.java @@ -45,14 +45,6 @@ public final class VariableIndex { default: throw new UnsupportedOperationException("Unknown index type " + type); } } - public final Instruction toInit() { - switch (type) { - case CAPTURES: throw new UnsupportedOperationException("Unknown index type " + type); - case CAPTURABLES: return Instruction.storeVar(index, false, true); - case LOCALS: return Instruction.storeVar(index, false, true); - default: throw new UnsupportedOperationException("Unknown index type " + type); - } - } public VariableIndex(VariableIndex.IndexType type, int index) { this.type = type; 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 9943083..414b4d2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/scope/VariableList.java @@ -13,7 +13,7 @@ public final class VariableList implements Iterable { public VariableNode next; public VariableNode prev; public int index; - public int indexIteration; + public int indexIteration = -1; public VariableList list() { return VariableList.this; } @@ -120,6 +120,7 @@ public final class VariableList implements Iterable { node.prev = null; varMap.remove(node.var); + node.var.setIndexSupplier(null); return node.var; } @@ -178,7 +179,10 @@ public final class VariableList implements Iterable { */ public VariableList(IndexType type, VariableList prev) { this.indexType = type; - this.offset = prev::size; + this.offset = () -> { + if (prev.offset != null) return prev.offset.getAsInt() + prev.size(); + else return prev.size(); + }; } public VariableList(IndexType type) { this.indexType = type; -- 2.45.2 From 39eb6ffac51e3ed949aae6bd0e2a1429acbc31a4 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:48:30 +0200 Subject: [PATCH 30/41] fix: do variable inits properly --- .../topchetoeu/jscript/compilation/FunctionNode.java | 2 +- .../jscript/compilation/VariableDeclareNode.java | 2 +- .../jscript/compilation/control/ForInNode.java | 2 +- .../jscript/compilation/control/TryNode.java | 2 +- .../jscript/compilation/values/VariableNode.java | 12 +++--------- .../values/operations/VariableAssignNode.java | 4 ++-- 6 files changed, 9 insertions(+), 15 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 38da50a..30878d7 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -38,7 +38,7 @@ public abstract class FunctionNode extends Node { var index = scope.define(param.name); target.add(Instruction.loadArg(i++)); - target.add(index.index().toInit()); + target.add(index.index().toSet(false)); } // if (selfName != null && !scope.has(selfName, false)) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index de32404..4ef5076 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -27,7 +27,7 @@ public class VariableDeclareNode extends Node { for (var entry : values) { if (entry.value != null) { entry.value.compile(target, true); - target.add(VariableNode.toInit(target, loc(), entry.var.name)); + target.add(VariableNode.toSet(target, loc(), entry.var.name, false, true)); } } 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 17de917..082ef6d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -34,7 +34,7 @@ public class ForInNode extends Node { int mid = target.temp(); target.add(Instruction.loadMember("value")).setLocation(binding.loc()); - target.add(VariableNode.toInit(target, loc(), binding.name)); + target.add(VariableNode.toSet(target, loc(), binding.name, false, true)); target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); 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 f9e8846..acce411 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -43,7 +43,7 @@ public class TryNode extends Node { if (captureName != null) { var catchVar = target.scope.defineCatch(captureName); target.add(Instruction.loadError()); - target.add(catchVar.index().toInit()); + target.add(catchVar.index().toSet(false)); catchBody.compile(target, false); target.scope.undefineCatch(); } 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 ca32e06..f8b3016 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -20,7 +20,7 @@ public class VariableNode extends Node implements ChangeTarget { } @Override public void afterAssign(CompileResult target, boolean pollute) { - target.add(VariableNode.toSet(target, loc(), name, pollute)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)); } @Override public void compile(CompileResult target, boolean pollute) { @@ -40,17 +40,11 @@ public class VariableNode extends Node implements ChangeTarget { return toGet(target, loc, name, true, false); } - public static Instruction toInit(CompileResult target, Location loc, String name) { - var i = target.scope.get(name, false); - - if (i != null) return i.index().toInit(); - else return Instruction.globSet(name, false, true); - } - public static Instruction toSet(CompileResult target, Location loc, String name, boolean keep) { + public static Instruction toSet(CompileResult target, Location loc, String name, boolean keep, boolean init) { var i = target.scope.get(name, false); if (i != null) return i.index().toSet(keep); - else return Instruction.globSet(name, keep, false); + else return Instruction.globSet(name, keep, init); } public VariableNode(Location loc, String name) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index 9f11b5b..940a27c 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -18,11 +18,11 @@ public class VariableAssignNode extends Node { target.add(VariableNode.toGet(target, loc(), name)); FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.operation(operation)); - target.add(VariableNode.toSet(target, loc(), name, pollute)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)); } else { FunctionNode.compileWithName(value, target, true, name); - target.add(VariableNode.toSet(target, loc(), name, pollute)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)); } } -- 2.45.2 From 5c2fd00bfbf34aac4e1159d2ef95d1975cbd099a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:48:49 +0200 Subject: [PATCH 31/41] fix: add location data for LOAD_FUNCs --- .../topchetoeu/jscript/compilation/FunctionStatementNode.java | 4 ++-- .../me/topchetoeu/jscript/compilation/FunctionValueNode.java | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 698f3dd..1e8f7ac 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -19,8 +19,8 @@ public class FunctionStatementNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, null)); - target.add(Instruction.loadFunc(id, name, captures(id, target))); - target.add(VariableNode.toInit(target, end, this.name)); + target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc());; + target.add(VariableNode.toSet(target, end, this.name, false, true)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index 3d5794a..d9ea9ff 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -14,7 +14,7 @@ public class FunctionValueNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { var id = target.addChild(compileBody(target, name, name)); - target.add(Instruction.loadFunc(id, name, captures(id, target))); + target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc()); } public FunctionValueNode(Location loc, Location end, List params, CompoundNode body, String name) { -- 2.45.2 From 3c13799c2f558fa550ca501c0cef862f4de0dbb0 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:49:04 +0200 Subject: [PATCH 32/41] feat: make function logging configurable --- .../topchetoeu/jscript/compilation/CompileResult.java | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 89e5b76..931c092 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -15,6 +15,8 @@ import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.compilation.scope.FunctionScope; public final class CompileResult { + public static final Key DEBUG_LOG = new Key<>(); + public static final class ChildData { public final int id; public final CompileResult result; @@ -85,7 +87,14 @@ public final class CompileResult { var instrRes = instructions(); - for (var instr : instrRes) System.out.println(instr); + if (env.has(DEBUG_LOG)) { + System.out.println("================= BODY ================="); + System.out.println("LOCALS: " + scope.localsCount()); + System.out.println("CAPTURABLES: " + scope.capturablesCount()); + System.out.println("CAPTURES: " + scope.capturesCount()); + + for (var instr : instrRes) System.out.println(instr); + } return new FunctionBody( scope.localsCount(), scope.capturablesCount(), scope.capturesCount(), -- 2.45.2 From ba7505e148040218f6385da41ccf0617abcd24fe Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Sun, 24 Nov 2024 12:49:31 +0200 Subject: [PATCH 33/41] fix: globalThis and for-in not parsed --- .../java/me/topchetoeu/jscript/compilation/JavaScript.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 8217fa6..c129808 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -18,6 +18,7 @@ import me.topchetoeu.jscript.compilation.control.ContinueNode; import me.topchetoeu.jscript.compilation.control.DebugNode; import me.topchetoeu.jscript.compilation.control.DeleteNode; import me.topchetoeu.jscript.compilation.control.DoWhileNode; +import me.topchetoeu.jscript.compilation.control.ForInNode; import me.topchetoeu.jscript.compilation.control.ForNode; import me.topchetoeu.jscript.compilation.control.IfNode; import me.topchetoeu.jscript.compilation.control.ReturnNode; @@ -28,6 +29,7 @@ import me.topchetoeu.jscript.compilation.control.WhileNode; import me.topchetoeu.jscript.compilation.scope.FunctionScope; import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArrayNode; +import me.topchetoeu.jscript.compilation.values.GlobalThisNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; import me.topchetoeu.jscript.compilation.values.SuperNode; @@ -114,6 +116,7 @@ public final class JavaScript { if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); if (id.result.equals("super")) return ParseRes.res(new SuperNode(loc), n); if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); + if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); return ParseRes.failed(); } @@ -192,6 +195,7 @@ public final class JavaScript { IfNode::parse, WhileNode::parse, SwitchNode::parse, + ForInNode::parse, ForNode::parse, DoWhileNode::parse, TryNode::parse, -- 2.45.2 From 4992d0211b1abf72731f6a545fbbe91a5bdf2112 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:15:15 +0200 Subject: [PATCH 34/41] fix: nasty issues with compilation --- src/compiler/main.ts | 235 ++++++++++++++++++ .../jscript/compilation/CompileResult.java | 21 +- .../jscript/compilation/CompoundNode.java | 3 + .../jscript/compilation/FunctionNode.java | 23 +- .../compilation/FunctionStatementNode.java | 6 +- .../compilation/FunctionValueNode.java | 8 +- .../jscript/compilation/JavaScript.java | 4 +- .../topchetoeu/jscript/compilation/Node.java | 2 + .../compilation/VariableDeclareNode.java | 5 + .../compilation/control/BreakNode.java | 5 +- .../compilation/control/ContinueNode.java | 3 + .../compilation/control/DebugNode.java | 3 + .../compilation/control/DeleteNode.java | 4 + .../compilation/control/DoWhileNode.java | 4 + .../compilation/control/ForInNode.java | 4 + .../jscript/compilation/control/ForNode.java | 32 +-- .../jscript/compilation/control/IfNode.java | 6 +- .../compilation/control/ReturnNode.java | 3 + .../compilation/control/SwitchNode.java | 10 +- .../compilation/control/ThrowNode.java | 3 + .../jscript/compilation/control/TryNode.java | 6 +- .../compilation/control/WhileNode.java | 4 + .../compilation/members/FieldMemberNode.java | 5 + .../jscript/compilation/members/Member.java | 1 + .../members/PropertyMemberNode.java | 10 +- .../compilation/values/ArgumentsNode.java | 3 + .../jscript/compilation/values/ArrayNode.java | 4 + .../compilation/values/GlobalThisNode.java | 3 + .../compilation/values/ObjectNode.java | 4 + .../jscript/compilation/values/RegexNode.java | 4 +- .../jscript/compilation/values/SuperNode.java | 17 -- .../jscript/compilation/values/ThisNode.java | 3 + .../compilation/values/VariableNode.java | 3 + .../values/constants/BoolNode.java | 3 + .../values/constants/NullNode.java | 3 + .../values/constants/NumberNode.java | 3 + .../values/constants/StringNode.java | 3 + .../values/operations/AssignNode.java | 5 + .../values/operations/CallNode.java | 5 + .../values/operations/ChangeNode.java | 5 + .../values/operations/DiscardNode.java | 4 + .../values/operations/IndexNode.java | 5 + .../values/operations/LazyAndNode.java | 5 + .../values/operations/LazyOrNode.java | 5 + .../values/operations/OperationNode.java | 4 + .../values/operations/PostfixNode.java | 4 + .../values/operations/TypeofNode.java | 4 + .../values/operations/VariableAssignNode.java | 4 + .../jscript/runtime/SimpleRepl.java | 4 +- src/main/resources/lib/index.js | 86 +++---- 50 files changed, 487 insertions(+), 116 deletions(-) create mode 100644 src/compiler/main.ts delete mode 100644 src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java diff --git a/src/compiler/main.ts b/src/compiler/main.ts new file mode 100644 index 0000000..a4063b3 --- /dev/null +++ b/src/compiler/main.ts @@ -0,0 +1,235 @@ +import { parse } from "acorn"; +import {} from "acorn"; +import { traverse } from "estraverse"; +import { Declaration, Identifier, Node, VariableDeclaration } from "estree"; + +enum VariableType { + Var, + Let, + Const, +} + +class Variable { + public constructor( + public readonly scope: Scope, + public readonly name: string, + public readonly type: VariableType, + public readonly readers = new Set(), + public readonly writers = new Set(), + ) { } + + public child(scope: Scope) { + return new Variable(scope, this.name, this.type, this.readers, this.writers); + } + + public get writable() { + return this.type !== VariableType.Const; + } +} + +class Scope { + private _locals = new Set(); + private _capturables = new Set(); + private _captures = new Set(); + + private _localNames = new Map(); + private _captureNames = new Map(); + private _parentToChild = new Map(); + private _childToParent = new Map(); + + public get locals() { + return Iterator.from(this._locals.values()); + } + + private _addCapture(v?: Variable) { + if (v != null && this._locals.delete(v)) { + this._capturables.add(v); + } + + return v; + } + + public capture(name: string) { + if (this._localNames.has(name)) return this._addCapture(this._localNames.get(name)); + if (this._captureNames.has(name)) return this._addCapture(this._captureNames.get(name)); + + const parent = this.parent?.capture(name); + if (parent == null) return undefined; + + const child = parent.child(this); + + this._parentToChild.set(parent, child); + this._childToParent.set(child, parent); + this._captures.add(child); + this._captureNames.set(child.name, child); + } + + public add(name: string, type: VariableType) { + let res = this.get(name, false); + if (res != null) return res; + + res = new Variable(this, name, type); + this._locals.add(res); + this._localNames.set(name, res); + return res; + } + public get(name: string, capture = true): Variable | undefined { + if (this._localNames.has(name)) return this._localNames.get(name); + if (this._captureNames.has(name)) return this._captureNames.get(name); + + if (capture) this.parent?.capture(name); + else return undefined; + } + + public constructor( + public readonly major: boolean, + public readonly node: Node, + public readonly parent?: Scope, + ) { } +} + +class BiMap implements Iterable<[A, B]> { + private _first = new Map(); + private _second = new Map(); + + public get(val: A): B; + public get(val: B): A; + public get(val: any) { + if (this._first.has(val)) return this._first.get(val); + if (this._second.has(val)) return this._second.get(val); + if (this._same.has(val)) return val; + return undefined; + } + + public set(a: A, b: B) { + this._first.set(a, b); + this._second.set(b, a); + + return this; + } + + public has(val: A | B) { + return this._first.has(val as any) || this._second.has(val as any); + } + + public delete(val: A | B) { + if (this._first.has(val as any)) { + const second = this._first.get(val as any)!; + this._first.delete(val as any); + this._second.delete(second); + + return true; + } + else if (this._second.has(val as any)) { + const first = this._second.get(val as any)!; + this._second.delete(val as any); + this._first.delete(first); + + return true; + } + else return false; + } + + public *[Symbol.iterator]() { + yield *this._first; + } + public *keys() { + yield *this._first.keys(); + } + public *values() { + yield *this._second.keys(); + } + public *entries() { + yield *this._first.entries(); + } +} + +class ResolutionContext { + public readonly variableRefs = new Map(); + public readonly declarations = new BiMap(); + + public resolveVariables() { + for (const el of this.variableRefs) { + } + } +} + +class NodeContext { + public node: Node = undefined!; + public path: Node[] = []; + + public atPath(i: number) { + return this.path[this.path.length - 1 - i]; + } + + public scope: Scope = undefined!; + public declType?: VariableType; +} + +interface Collector { + enter(ctx: NodeContext, root: ResolutionContext): void; + leave(ctx: NodeContext, root: ResolutionContext): void; +} + +function collect(node: Node, root: ResolutionContext, ...collectors: Collector[]) { + const nodeCtx = new NodeContext(); + const path: Node[] = []; + + traverse(node, { + enter(node) { + nodeCtx.node = node; + nodeCtx.path.push(node); + for (let i = 0; i < collectors.length; i++) { + collectors[i].enter(nodeCtx, root); + } + }, + leave(node) { + nodeCtx.node = node; + nodeCtx.path.pop(); + for (let i = 0; i < collectors.length; i++) { + collectors[i].leave(nodeCtx, root); + } + }, + }); +} + +function assertDefined(val: unknown): asserts val is {} { + if (val == null) throw new Error("Undefined or null expression"); +} + +const scopeCollector: Collector = { + enter(ctx, root) { + if (ctx.node.type === "BlockStatement") { + ctx.scope = new Scope(false, ctx.node, ctx.scope.parent); + } + else if (ctx.node.type === "VariableDeclaration") { + switch (ctx.node.kind) { + case "var": ctx.declType = VariableType.Var; break; + case "let": ctx.declType = VariableType.Let; break; + case "const": ctx.declType = VariableType.Const; break; + default: throw new Error(`Unknown variable type '${(ctx.node as any).kind}'`); + } + } + else if (ctx.node.type === "VariableDeclarator") { + ctx.scope. + } + else if (ctx.node.type === "ClassDeclaration") { + + } + else if (ctx.node.type === "Identifier") { + } + }, + leave(ctx, root) { + if (ctx.scope.node === ctx.node) { + assertDefined(ctx.scope.parent); + ctx.scope = ctx.scope.parent; + } + else if (ctx.node.type === "VariableDeclaration") { + ctx.declType = undefined; + } + }, +}; + + +const program = parse("const a = 10;", { ecmaVersion: "latest" }) as Node; +collect(program, domain, ...stage1); \ No newline at end of file diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java index 931c092..b70178d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompileResult.java @@ -1,7 +1,10 @@ package me.topchetoeu.jscript.compilation; import java.util.List; +import java.util.Map; + import java.util.ArrayList; +import java.util.HashMap; import java.util.LinkedList; import me.topchetoeu.jscript.common.FunctionBody; @@ -17,18 +20,10 @@ import me.topchetoeu.jscript.compilation.scope.FunctionScope; public final class CompileResult { public static final Key DEBUG_LOG = new Key<>(); - 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 children; + public final Map childrenMap = new HashMap<>(); + public final Map childrenIndices = new HashMap<>(); public final FunctionMapBuilder map; public final Environment env; public int length; @@ -69,9 +64,11 @@ public final class CompileResult { setLocationAndDebug(instructions.size() - 1, loc, type); } - public int addChild(CompileResult res) { + public CompileResult addChild(FunctionNode node, CompileResult res) { this.children.add(res); - return this.children.size() - 1; + this.childrenMap.put(node, res); + this.childrenIndices.put(node, this.children.size() - 1); + return res; } public Instruction[] instructions() { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index 66b2e6d..bd761eb 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -19,6 +19,9 @@ public class CompoundNode extends Node { @Override public void resolve(CompileResult target) { for (var stm : statements) stm.resolve(target); } + @Override public void compileFunctions(CompileResult target) { + for (var stm : statements) stm.compileFunctions(target); + } public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) { List statements = new ArrayList(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 30878d7..9499cda 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -21,19 +21,22 @@ public abstract class FunctionNode extends Node { public final String name(String fallback) { return this.name() != null ? this.name() : fallback; } - - protected final int[] captures(int id, CompileResult target) { - return target.children.get(id).scope.getCaptureIndices(); + protected final int[] captures(CompileResult target) { + return target.childrenMap.get(this).scope.getCaptureIndices(); } protected final Environment rootEnv(Environment env) { return env.get(JavaScript.COMPILE_ROOT); } - public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) { + @Override public void resolve(CompileResult target) { } + + public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String selfName) { var target = new CompileResult(env, scope, params.size()); var i = 0; + body.resolve(target); + for (var param : params) { var index = scope.define(param.name); @@ -44,17 +47,17 @@ public abstract class FunctionNode extends Node { // if (selfName != null && !scope.has(selfName, false)) { // var i = scope.defineSpecial(new Variable(selfName, true), end); - // target.add(Instruction.loadCalled()); - // target.add(_i -> i.index().toInit()); + // t.add(Instruction.loadCalled()); + // t.add(_i -> i.index().toInit()); // } - body.resolve(target); + body.compileFunctions(target); body.compile(target, lastReturn, BreakpointType.NONE); return target; - } - public final CompileResult compileBody(CompileResult parent, String name, String selfName) { - return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, name, selfName); + } + public final CompileResult compileBody(CompileResult parent, String selfName) { + return compileBody(rootEnv(parent.env).child(), new FunctionScope(parent.scope), false, selfName); } public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index 1e8f7ac..b8c4c85 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -16,10 +16,12 @@ public class FunctionStatementNode extends FunctionNode { @Override public void resolve(CompileResult target) { target.scope.define(new Variable(name, false)); } + @Override public void compileFunctions(CompileResult target) { + target.addChild(this, compileBody(target, name())); + } @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, name, null)); - target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc());; + target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc()); target.add(VariableNode.toSet(target, end, this.name, false, true)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java index d9ea9ff..05f2db3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionValueNode.java @@ -12,9 +12,13 @@ public class FunctionValueNode extends FunctionNode { @Override public String name() { return name; } + @Override public void compileFunctions(CompileResult target) { + target.addChild(this, compileBody(target, name())); + } + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { - var id = target.addChild(compileBody(target, name, name)); - target.add(Instruction.loadFunc(id, name, captures(id, target))).setLocation(loc()); + target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc()); + if (!pollute) target.add(Instruction.discard()); } public FunctionValueNode(Location loc, Location end, List 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 c129808..7fda233 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -32,7 +32,6 @@ import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.GlobalThisNode; import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.RegexNode; -import me.topchetoeu.jscript.compilation.values.SuperNode; import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.constants.BoolNode; @@ -114,7 +113,6 @@ public final class JavaScript { if (id.result.equals("false")) return ParseRes.res(new BoolNode(loc, false), n); if (id.result.equals("null")) return ParseRes.res(new NullNode(loc), n); if (id.result.equals("this")) return ParseRes.res(new ThisNode(loc), n); - if (id.result.equals("super")) return ParseRes.res(new SuperNode(loc), n); if (id.result.equals("arguments")) return ParseRes.res(new ArgumentsNode(loc), n); if (id.result.equals("globalThis")) return ParseRes.res(new GlobalThisNode(loc), n); @@ -260,7 +258,7 @@ public final class JavaScript { env.add(COMPILE_ROOT, env); var func = new FunctionValueNode(null, null, Arrays.asList(), new CompoundNode(null, statements), null); - var res = func.compileBody(env, new FunctionScope(true), true, null, null); + var res = func.compileBody(env, new FunctionScope(true), true, null); return res; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/Node.java b/src/main/java/me/topchetoeu/jscript/compilation/Node.java index 7da1c3e..033aa44 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/Node.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/Node.java @@ -17,6 +17,8 @@ public abstract class Node { compile(target, pollute, BreakpointType.NONE); } + public abstract void compileFunctions(CompileResult target); + public Location loc() { return loc; } public void setLoc(Location loc) { this.loc = loc; } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index 4ef5076..cb7170d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -23,6 +23,11 @@ public class VariableDeclareNode extends Node { target.scope.define(entry.var.name); } } + @Override public void compileFunctions(CompileResult target) { + for (var pair : values) { + if (pair.value != null) pair.value.compileFunctions(target); + } + } @Override public void compile(CompileResult target, boolean pollute) { for (var entry : values) { if (entry.value != null) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java index bd2e877..a52a47d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/BreakNode.java @@ -14,7 +14,10 @@ import me.topchetoeu.jscript.compilation.Node; public class BreakNode extends Node { public final String label; - @Override public void compile(CompileResult target, boolean pollute) { + @Override public void compileFunctions(CompileResult target) { + } + + @Override public void compile(CompileResult target, boolean pollute) { if (!LabelContext.getBreak(target.env).jump(target)) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); else throw new SyntaxException(loc(), "Illegal break statement"); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java index f0b13ed..8531211 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ContinueNode.java @@ -14,6 +14,9 @@ import me.topchetoeu.jscript.compilation.Node; public class ContinueNode extends Node { public final String label; + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (!LabelContext.getCont(target.env).jump(target)) { if (label != null) throw new SyntaxException(loc(), String.format("Undefined label '%s'", label)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java index 9464e9d..bb0bd36 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DebugNode.java @@ -10,6 +10,9 @@ import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.Node; public class DebugNode extends Node { + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.debug()); if (pollute) target.add(Instruction.pushUndefined()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java index 6df811d..60886bc 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DeleteNode.java @@ -16,6 +16,10 @@ public class DeleteNode extends Node { public final Node key; public final Node value; + @Override public void compileFunctions(CompileResult target) { + key.compileFunctions(target); + value.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { value.compile(target, true); key.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index 6f64061..559f96f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -16,6 +16,10 @@ public class DoWhileNode extends Node { public final Node condition, body; public final String label; + @Override public void compileFunctions(CompileResult target) { + condition.compileFunctions(target); + body.compileFunctions(target); + } @Override public void resolve(CompileResult target) { body.resolve(target); } 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 082ef6d..5e915e7 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -25,6 +25,10 @@ public class ForInNode extends Node { binding.resolve(target); } + @Override public void compileFunctions(CompileResult target) { + object.compileFunctions(target); + body.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { object.compile(target, true, BreakpointType.STEP_OVER); target.add(Instruction.keys(false, 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 8d82215..91825d5 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -23,29 +23,33 @@ public class ForNode extends Node { declaration.resolve(target); body.resolve(target); } + @Override public void compileFunctions(CompileResult target) { + declaration.compileFunctions(target); + assignment.compileFunctions(target); + condition.compileFunctions(target); + body.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { - var subtarget = target.subtarget(); + declaration.compile(target, false, BreakpointType.STEP_OVER); - declaration.compile(subtarget, false, BreakpointType.STEP_OVER); - - int start = subtarget.size(); - CompoundNode.compileMultiEntry(condition, subtarget, true, BreakpointType.STEP_OVER); - int mid = subtarget.temp(); + int start = target.size(); + CompoundNode.compileMultiEntry(condition, target, true, BreakpointType.STEP_OVER); + int mid = target.temp(); var end = new DeferredIntSupplier(); - LabelContext.pushLoop(subtarget.env, loc(), label, end, start); - CompoundNode.compileMultiEntry(body, subtarget, false, BreakpointType.STEP_OVER); + LabelContext.pushLoop(target.env, loc(), label, end, start); + CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); - CompoundNode.compileMultiEntry(assignment, subtarget, false, BreakpointType.STEP_OVER); - int endI = subtarget.size(); + CompoundNode.compileMultiEntry(assignment, target, false, BreakpointType.STEP_OVER); + int endI = target.size(); end.set(endI); - LabelContext.popLoop(subtarget.env, label); + LabelContext.popLoop(target.env, label); - subtarget.add(Instruction.jmp(start - endI)); - subtarget.set(mid, Instruction.jmpIfNot(endI - mid + 1)); - if (pollute) subtarget.add(Instruction.pushUndefined()); + target.add(Instruction.jmp(start - endI)); + target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); + if (pollute) target.add(Instruction.pushUndefined()); } 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/IfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java index bd75010..8564d03 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -20,7 +20,11 @@ public class IfNode extends Node { body.resolve(target); if (elseBody != null) elseBody.resolve(target); } - + @Override public void compileFunctions(CompileResult target) { + condition.compileFunctions(target); + body.compileFunctions(target); + if (elseBody != null) body.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) { condition.compile(target, true, breakpoint); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java index 3caccea..7e7d708 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ReturnNode.java @@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node; public class ReturnNode extends Node { public final Node value; + @Override public void compileFunctions(CompileResult target) { + if (value != null) value.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { if (value == null) target.add(Instruction.pushUndefined()); else value.compile(target, true); 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 db40293..cce1309 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -36,7 +36,15 @@ public class SwitchNode extends Node { @Override public void resolve(CompileResult target) { for (var stm : body) stm.resolve(target); } - + @Override public void compileFunctions(CompileResult target) { + value.compileFunctions(target); + for (var _case : cases) { + _case.value.compileFunctions(target); + } + for (var stm : body) { + stm.compileFunctions(target); + } + } @Override public void compile(CompileResult target, boolean pollute) { var caseToStatement = new HashMap(); var statementToIndex = new HashMap(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java index 2c6cb37..9f05e2f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ThrowNode.java @@ -12,6 +12,9 @@ import me.topchetoeu.jscript.compilation.Node; public class ThrowNode extends Node { public final Node value; + @Override public void compileFunctions(CompileResult target) { + value.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { value.compile(target, true); target.add(Instruction.throwInstr()).setLocation(loc()); 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 acce411..ffdef9e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -25,7 +25,11 @@ public class TryNode extends Node { if (catchBody != null) catchBody.resolve(target); if (finallyBody != null) finallyBody.resolve(target); } - + @Override public void compileFunctions(CompileResult target) { + tryBody.compileFunctions(target); + if (catchBody != null) catchBody.compileFunctions(target); + if (finallyBody != null) finallyBody.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute, BreakpointType bpt) { int replace = target.temp(); var endSuppl = new DeferredIntSupplier(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index 49c0509..c1ba16e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -20,6 +20,10 @@ public class WhileNode extends Node { @Override public void resolve(CompileResult target) { body.resolve(target); } + @Override public void compileFunctions(CompileResult target) { + condition.compileFunctions(target); + body.compileFunctions(target); + } @Override public void compile(CompileResult target, boolean pollute) { int start = target.size(); condition.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java index 7888d94..f369f65 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/FieldMemberNode.java @@ -17,6 +17,11 @@ public class FieldMemberNode implements Member { @Override public Location loc() { return loc; } + @Override public void compileFunctions(CompileResult target) { + key.compileFunctions(target); + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.dup()); key.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java index 8f48859..84ebf68 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/Member.java @@ -6,5 +6,6 @@ import me.topchetoeu.jscript.compilation.CompileResult; public interface Member { Location loc(); + void compileFunctions(CompileResult target); void compile(CompileResult target, boolean pollute); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java index 8e0dcce..4522750 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -32,12 +32,18 @@ public final class PropertyMemberNode extends FunctionNode implements Member { public boolean isGetter() { return argument == null; } public boolean isSetter() { return argument != null; } + @Override public void compileFunctions(CompileResult target) { + key.compileFunctions(target); + target.addChild(this, compileBody(target, null)); + } + @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { if (pollute) target.add(Instruction.dup()); key.compile(target, true); - var id = target.addChild(compileBody(target, name, null)); - target.add(Instruction.loadFunc(id, name(), captures(id, target))); + target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc()); + target.add(VariableNode.toSet(target, end, name(name), false, true)); + target.add(Instruction.defProp(isSetter())); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java index 104340f..2ed09d0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArgumentsNode.java @@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node; public class ArgumentsNode extends Node { + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.loadArgs()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java index 44db447..ccfeb75 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java @@ -15,6 +15,10 @@ import me.topchetoeu.jscript.compilation.Node; public class ArrayNode extends Node { public final Node[] statements; + @Override public void compileFunctions(CompileResult target) { + for (var stm : statements) stm.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadArr(statements.length)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java index 11823a3..ac83d7d 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/GlobalThisNode.java @@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node; public class GlobalThisNode extends Node { + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.loadGlob()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java index 8785416..7b41a28 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ObjectNode.java @@ -20,6 +20,10 @@ import me.topchetoeu.jscript.compilation.values.constants.StringNode; public class ObjectNode extends Node { public final List members; + @Override public void compileFunctions(CompileResult target) { + for (var member : members) member.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadObj()); for (var el : members) el.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java index 7ac7c51..26807d4 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java @@ -11,12 +11,14 @@ import me.topchetoeu.jscript.compilation.Node; public class RegexNode extends Node { public final String pattern, flags; + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { target.add(Instruction.loadRegex(pattern, flags)); if (!pollute) target.add(Instruction.discard()); } - public static ParseRes parse(Source src, int i) { var n = Parsing.skipEmpty(src, i); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java deleted file mode 100644 index b7284fb..0000000 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/SuperNode.java +++ /dev/null @@ -1,17 +0,0 @@ -package me.topchetoeu.jscript.compilation.values; - -import me.topchetoeu.jscript.common.SyntaxException; -import me.topchetoeu.jscript.common.parsing.Location; -import me.topchetoeu.jscript.compilation.CompileResult; -import me.topchetoeu.jscript.compilation.Node; - - -public class SuperNode extends Node { - @Override public void compile(CompileResult target, boolean pollute) { - throw new SyntaxException(loc(), "Unexpected 'super' reference here"); - } - - public SuperNode(Location loc) { - super(loc); - } -} diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java index ad74007..27a05b3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ThisNode.java @@ -7,6 +7,9 @@ import me.topchetoeu.jscript.compilation.Node; public class ThisNode extends Node { + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.loadThis()); } 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 f8b3016..39f4b99 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -13,6 +13,9 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; public class VariableNode extends Node implements ChangeTarget { public final String name; + @Override public void compileFunctions(CompileResult target) { + } + public String assignName() { return name; } @Override public void beforeChange(CompileResult target) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java index 8a5462b..4eab801 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/BoolNode.java @@ -8,6 +8,9 @@ import me.topchetoeu.jscript.compilation.Node; public class BoolNode extends Node { public final boolean value; + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java index cae8bd6..3b1ff02 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NullNode.java @@ -6,6 +6,9 @@ import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.Node; public class NullNode extends Node { + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushNull()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java index 5afd40e..d76a949 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/NumberNode.java @@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node; public class NumberNode extends Node { public final double value; + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java index e50d82b..ede91c3 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/constants/StringNode.java @@ -11,6 +11,9 @@ import me.topchetoeu.jscript.compilation.Node; public class StringNode extends Node { public final String value; + @Override public void compileFunctions(CompileResult target) { + } + @Override public void compile(CompileResult target, boolean pollute) { if (pollute) target.add(Instruction.pushValue(value)); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/AssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/AssignNode.java index 20ccbb2..6860f09 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/AssignNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/AssignNode.java @@ -12,6 +12,11 @@ public class AssignNode extends Node implements AssignTarget { public final AssignTarget assignable; public final Node value; + @Override public void compileFunctions(CompileResult target) { + ((Node)assignable).compileFunctions(target); + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { if (assignable instanceof AssignNode other) throw new SyntaxException(other.loc(), "Assign deconstructor not allowed here"); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java index af2fbe2..150ea36 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/CallNode.java @@ -17,6 +17,11 @@ public class CallNode extends Node { public final Node[] args; public final boolean isNew; + @Override public void compileFunctions(CompileResult target) { + func.compileFunctions(target); + for (var arg : args) arg.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { if (!isNew && func instanceof IndexNode indexNode) { var obj = indexNode.object; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java index e5c316c..160d875 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/ChangeNode.java @@ -17,6 +17,11 @@ public class ChangeNode extends Node { public final Node value; public final Operation op; + @Override public void compileFunctions(CompileResult target) { + ((Node)changable).compileFunctions(target); + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { changable.beforeChange(target); value.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java index eaf7b89..ba4b697 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java @@ -13,6 +13,10 @@ import me.topchetoeu.jscript.compilation.Node; public class DiscardNode extends Node { public final Node value; + @Override public void compileFunctions(CompileResult target) { + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { if (value != null) value.compile(target, false); if (pollute) target.add(Instruction.pushUndefined()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java index ea2544e..d108eea 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/IndexNode.java @@ -17,6 +17,11 @@ public class IndexNode extends Node implements ChangeTarget { public final Node object; public final Node index; + @Override public void compileFunctions(CompileResult target) { + object.compileFunctions(target); + index.compileFunctions(target); + } + @Override public void beforeAssign(CompileResult target) { object.compile(target, true); indexStorePushKey(target, index); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java index 318751e..152551a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyAndNode.java @@ -12,6 +12,11 @@ import me.topchetoeu.jscript.compilation.Node; public class LazyAndNode extends Node { public final Node first, second; + @Override public void compileFunctions(CompileResult target) { + first.compileFunctions(target); + second.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java index 9b1e5a9..f75864a 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/LazyOrNode.java @@ -13,6 +13,11 @@ import me.topchetoeu.jscript.compilation.Node; public class LazyOrNode extends Node { public final Node first, second; + @Override public void compileFunctions(CompileResult target) { + first.compileFunctions(target); + second.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { first.compile(target, true); if (pollute) target.add(Instruction.dup()); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java index 05b8fa9..b9ac637 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/OperationNode.java @@ -105,6 +105,10 @@ public class OperationNode extends Node { public final Node[] args; public final Operation operation; + @Override public void compileFunctions(CompileResult target) { + for (var arg : args) arg.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { for (var arg : args) { arg.compile(target, true); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/PostfixNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/PostfixNode.java index 14b5f77..492b581 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/PostfixNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/PostfixNode.java @@ -12,6 +12,10 @@ import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; import me.topchetoeu.jscript.compilation.values.constants.NumberNode; public class PostfixNode extends ChangeNode { + @Override public void compileFunctions(CompileResult target) { + ((Node)changable).compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { super.compile(target, pollute); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java index ba89965..68683dd 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/TypeofNode.java @@ -14,6 +14,10 @@ import me.topchetoeu.jscript.compilation.values.VariableNode; public class TypeofNode extends Node { public final Node value; + @Override public void compileFunctions(CompileResult target) { + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { if (value instanceof VariableNode varNode) { target.add(VariableNode.toGet(target, varNode.loc(), varNode.name, true, true)); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index 940a27c..9be0d0e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -13,6 +13,10 @@ public class VariableAssignNode extends Node { public final Node value; public final Operation operation; + @Override public void compileFunctions(CompileResult target) { + value.compileFunctions(target); + } + @Override public void compile(CompileResult target, boolean pollute) { if (operation != null) { target.add(VariableNode.toGet(target, loc(), name)); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java index f36e833..eefc6eb 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/SimpleRepl.java @@ -13,6 +13,7 @@ import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.common.environment.Key; import me.topchetoeu.jscript.common.json.JSON; import me.topchetoeu.jscript.common.parsing.Filename; +import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.runtime.debug.DebugContext; import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.Member.FieldMember; @@ -278,7 +279,7 @@ public class SimpleRepl { int[] i = new int[1]; res.defineOwnMember(env, "setGlobalPrototypes", new NativeFunction(args -> { - var obj = (ObjectValue)args.get(1); + var obj = (ObjectValue)args.get(0); setProto(args.env, env, Value.OBJECT_PROTO, obj, "object"); setProto(args.env, env, Value.FUNCTION_PROTO, obj, "function"); @@ -313,6 +314,7 @@ public class SimpleRepl { environment.add(EventLoop.KEY, engine); environment.add(DebugContext.KEY, new DebugContext()); environment.add(Compiler.KEY, Compiler.DEFAULT); + // environment.add(CompileResult.DEBUG_LOG); var glob = Value.global(environment); diff --git a/src/main/resources/lib/index.js b/src/main/resources/lib/index.js index cbdd81f..33708fb 100644 --- a/src/main/resources/lib/index.js +++ b/src/main/resources/lib/index.js @@ -1,20 +1,13 @@ -return; (function main() { - var __extends = (this && this.__extends) || (function () { - var extendStatics = function (d, b) { - extendStatics = Object.setPrototypeOf || - ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || - function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; }; - return extendStatics(d, b); - }; - return function (d, b) { - if (typeof b !== "function" && b !== null) - throw new TypeError("Class extends value " + String(b) + " is not a constructor or null"); - extendStatics(d, b); - function __() { this.constructor = d; } - d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); - }; - })(); + function extend(derived, base) { + if (base == null) { + object.setPrototype(derived.prototype, null); + } + else { + object.setPrototype(derived, base); + object.setPrototype(derived.prototype, base.prototype); + } + } var target = arguments[0]; var primordials = arguments[1]; @@ -68,9 +61,8 @@ return; parse: function (val) { throw new Error("JSON parse not polyfillable"); }, } - var setGlobalPrototypes = primordials.setGlobalPrototype; + var setGlobalPrototypes = primordials.setGlobalPrototypes; var compile = primordials.compile; - var setIntrinsic = primordials.setIntrinsic; var valueKey = symbol.makeSymbol("Primitive.value"); var undefined = void 0; target.undefined = undefined; @@ -88,26 +80,22 @@ return; throw new TypeError(name + " requires that '" + arg + "' be a " + constr.name); } function wrapIndex(i, len) { } - var Symbol = /** @class */ (function () { - function Symbol(name) { - if (name === undefined) name = ""; - return symbol.makeSymbol(name); - } - Symbol.prototype.toString = function () { - return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; - }; - Symbol.prototype.valueOf = function () { - return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); - }; - Symbol.for = function (name) { - return symbol.getSymbol(name + ""); - }; - Symbol.keyFor = function (value) { - return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor")); - }; - return Symbol; - }()); - ; + function Symbol(name) { + if (name === undefined) name = ""; + return symbol.makeSymbol(name); + } + Symbol.prototype.toString = function () { + return "Symbol(" + unwrapThis(this, "symbol", Symbol, "Symbol.prototype.toString").description + ")"; + }; + Symbol.prototype.valueOf = function () { + return unwrapThis(this, "symbol", Symbol, "Symbol.prototype.valueOf"); + }; + Symbol.for = function (name) { + return symbol.getSymbol(name + ""); + }; + Symbol.keyFor = function (value) { + return symbol.getSymbolKey(unwrapThis(value, "symbol", Symbol, "Symbol.keyFor")); + }; object.defineProperty(Symbol.prototype, "desc", false, true, function () { return symbol.getSymbolDescriptor(unwrapThis(this, "symbol", Symbol, "Symbol.prototype.description")); }); @@ -120,7 +108,8 @@ return; object.defineField(Symbol, "split", false, false, false, Symbol("Symbol.split")); object.defineField(Symbol, "toStringTag", false, false, false, Symbol("Symbol.toStringTag")); func.setConstructable(Symbol, false); - + target.Symbol = Symbol; + function Number(value) { if (func.invokeType(arguments, this) === "call") { if (arguments.length === 0) return 0; @@ -290,6 +279,7 @@ return; return obj; }; func.setCallable(Object, true); + extend(Object, null); object.setPrototype(Object.prototype, null); target.Object = Object; @@ -351,7 +341,7 @@ return; } func.setCallable(Array, true); target.Array = Array; - + function Error(msg) { if (msg === void 0) { msg = ""; } if (func.invokeType(arguments, this) === "call") @@ -369,8 +359,8 @@ return; object.defineField(Error.prototype, "message", true, false, true, ""); func.setCallable(Error, true); target.Error = Error; - - __extends(SyntaxError, Error); + + extend(SyntaxError, Error); function SyntaxError(msg) { if (func.invokeType(arguments, this) === "call") return new SyntaxError(msg); @@ -379,8 +369,8 @@ return; object.defineField(SyntaxError.prototype, "name", true, false, true, "SyntaxError"); func.setCallable(SyntaxError, true); target.SyntaxError = SyntaxError; - - __extends(TypeError, Error); + + extend(TypeError, Error); function TypeError(msg) { if (func.invokeType(arguments, this) === "call") return new TypeError(msg); @@ -389,8 +379,8 @@ return; object.defineField(TypeError.prototype, "name", true, false, true, "TypeError"); func.setCallable(TypeError, true); target.TypeError = TypeError; - - __extends(RangeError, Error); + + extend(RangeError, Error); function RangeError(msg) { if (func.invokeType(arguments, this) === "call") return new RangeError(msg); @@ -399,9 +389,9 @@ return; object.defineField(RangeError.prototype, "name", true, false, true, "RangeError"); func.setCallable(RangeError, true); target.RangeError = RangeError; - + target.uint8 = primordials.uint8; - + setGlobalPrototypes({ string: String.prototype, number: Number.prototype, -- 2.45.2 From 611be55bbbf205931c4ab35e0d24beb354b6b4b7 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:15:38 +0200 Subject: [PATCH 35/41] fix: should throw engine exceptions, not java exceptions --- .../jscript/runtime/values/functions/FunctionValue.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index 3038536..5b3fcc7 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -7,6 +7,7 @@ import java.util.List; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.debug.DebugContext; +import me.topchetoeu.jscript.runtime.exceptions.EngineException; import me.topchetoeu.jscript.runtime.values.KeyCache; import me.topchetoeu.jscript.runtime.values.Member; import me.topchetoeu.jscript.runtime.values.Value; @@ -55,11 +56,11 @@ public abstract class FunctionValue extends ObjectValue { @Override public String toString() { return String.format("function %s(...)", name); } @Override public Value apply(Environment env, Value self, Value... args) { - if (!enableApply) throw new RuntimeException("Function cannot be applied"); + if (!enableApply) throw EngineException.ofType("Function cannot be applied"); return onCall(env, false, self, args); } @Override public Value construct(Environment env, Value self, Value... args) { - if (!enableConstruct) throw new RuntimeException("Function cannot be constructed"); + if (!enableConstruct) throw EngineException.ofType("Function cannot be constructed"); return onCall(env, true, self, args); } -- 2.45.2 From 28679f44d5d90361910b160679c6f5b22272b169 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:15:51 +0200 Subject: [PATCH 36/41] fix: symbols not stringified properly --- .../jscript/runtime/values/primitives/SymbolValue.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java index 15e96c1..08d37d0 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/SymbolValue.java @@ -1,6 +1,9 @@ package me.topchetoeu.jscript.runtime.values.primitives; +import java.util.Arrays; import java.util.HashMap; +import java.util.HashSet; +import java.util.List; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.exceptions.EngineException; @@ -34,6 +37,11 @@ public final class SymbolValue extends PrimitiveValue { else return "Symbol(" + value + ")"; } + @Override + public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(toString()); + } + public SymbolValue(String value) { this.value = value; } -- 2.45.2 From 54d55814afd222d03eb1ca5e24f35571c2e583f0 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 22:16:24 +0200 Subject: [PATCH 37/41] fix: errors with out of range arguments --- .../java/me/topchetoeu/jscript/runtime/InstructionRunner.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index cbb6857..dd44deb 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -469,7 +469,9 @@ public class InstructionRunner { } private static Value execLoadArg(Environment env, Instruction instr, Frame frame) { - frame.push(frame.args[(int)instr.get(0)]); + int i = instr.get(0); + if (i >= frame.args.length) frame.push(Value.UNDEFINED); + else frame.push(frame.args[i]); frame.codePtr++; return null; } -- 2.45.2 From 2e8e123ec43884ddbc83397d653c5d4bce309c8a Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:37:08 +0200 Subject: [PATCH 38/41] small parser fixes --- .../jscript/common/parsing/Parsing.java | 1 + .../jscript/compilation/CompoundNode.java | 15 +----- .../jscript/compilation/FunctionNode.java | 13 ++--- .../compilation/FunctionStatementNode.java | 2 +- .../jscript/compilation/JavaScript.java | 2 +- .../compilation/VariableDeclareNode.java | 2 +- .../compilation/control/DoWhileNode.java | 1 + .../compilation/control/ForInNode.java | 5 +- .../jscript/compilation/control/ForNode.java | 22 ++++---- .../jscript/compilation/control/IfNode.java | 8 +-- .../compilation/control/SwitchNode.java | 5 +- .../jscript/compilation/control/TryNode.java | 4 +- .../compilation/control/WhileNode.java | 3 +- .../members/PropertyMemberNode.java | 7 --- .../jscript/compilation/values/ArrayNode.java | 4 +- .../jscript/compilation/values/RegexNode.java | 51 +++++++++++-------- .../compilation/values/VariableNode.java | 2 +- .../values/operations/DiscardNode.java | 2 +- .../values/operations/VariableAssignNode.java | 4 +- 19 files changed, 73 insertions(+), 80 deletions(-) diff --git a/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java b/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java index 96886d8..1af3a10 100644 --- a/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java +++ b/src/main/java/me/topchetoeu/jscript/common/parsing/Parsing.java @@ -113,6 +113,7 @@ public class Parsing { return ParseRes.res((char)newC, n); } else if (c == '\n') return ParseRes.res(null, n); + else n--; } return ParseRes.res(src.at(i + n), n + 1); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java index bd761eb..0754795 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/CompoundNode.java @@ -23,7 +23,7 @@ public class CompoundNode extends Node { for (var stm : statements) stm.compileFunctions(target); } - public void compile(CompileResult target, boolean pollute, boolean singleEntry, BreakpointType type) { + public void compile(CompileResult target, boolean pollute, BreakpointType type) { List statements = new ArrayList(); for (var stm : this.statements) { @@ -47,10 +47,6 @@ public class CompoundNode extends Node { } } - @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) { - compile(target, pollute, true, type); - } - public CompoundNode setEnd(Location loc) { this.end = loc; return this; @@ -61,15 +57,6 @@ public class CompoundNode extends Node { this.statements = statements; } - public static void compileMultiEntry(Node node, CompileResult target, boolean pollute, BreakpointType type) { - if (node instanceof CompoundNode comp) { - comp.compile(target, pollute, false, type); - } - else { - node.compile(target, pollute, type); - } - } - public static ParseRes parseComma(Source src, int i, Node prev, int precedence) { if (precedence > 1) return ParseRes.failed(); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java index 9499cda..107d0d5 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionNode.java @@ -37,12 +37,7 @@ public abstract class FunctionNode extends Node { body.resolve(target); - for (var param : params) { - var index = scope.define(param.name); - - target.add(Instruction.loadArg(i++)); - target.add(index.index().toSet(false)); - } + for (var param : params) scope.define(param.name); // if (selfName != null && !scope.has(selfName, false)) { // var i = scope.defineSpecial(new Variable(selfName, true), end); @@ -52,6 +47,12 @@ public abstract class FunctionNode extends Node { // } body.compileFunctions(target); + + for (var param : params) { + target.add(Instruction.loadArg(i++)).setLocation(param.loc()); + target.add(scope.define(param.name).index().toSet(false)).setLocation(param.loc()); + } + body.compile(target, lastReturn, BreakpointType.NONE); return target; diff --git a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java index b8c4c85..97a4e09 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/FunctionStatementNode.java @@ -22,7 +22,7 @@ public class FunctionStatementNode extends FunctionNode { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc()); - target.add(VariableNode.toSet(target, end, this.name, false, true)); + target.add(VariableNode.toSet(target, end, this.name, false, true)).setLocation(loc()); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java index 7fda233..829d1f0 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/JavaScript.java @@ -193,8 +193,8 @@ public final class JavaScript { IfNode::parse, WhileNode::parse, SwitchNode::parse, - ForInNode::parse, ForNode::parse, + ForInNode::parse, DoWhileNode::parse, TryNode::parse, CompoundNode::parse, diff --git a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java index cb7170d..083bf57 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/VariableDeclareNode.java @@ -32,7 +32,7 @@ public class VariableDeclareNode extends Node { for (var entry : values) { if (entry.value != null) { entry.value.compile(target, true); - target.add(VariableNode.toSet(target, loc(), entry.var.name, false, true)); + target.add(VariableNode.toSet(target, loc(), entry.var.name, false, true)).setLocation(loc()); } } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java index 559f96f..704218b 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/DoWhileNode.java @@ -62,6 +62,7 @@ public class DoWhileNode extends Node { var bodyRes = JavaScript.parseStatement(src, i + n); if (!bodyRes.isSuccess()) return bodyRes.chainError(src.loc(i + n), "Expected a do-while body."); n += bodyRes.n; + n += Parsing.skipEmpty(src, i + n); if (!Parsing.isIdentifier(src, i + n, "while")) return ParseRes.failed(); n += 5; 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 5e915e7..b176632 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForInNode.java @@ -7,7 +7,6 @@ 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.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; @@ -38,14 +37,14 @@ public class ForInNode extends Node { int mid = target.temp(); target.add(Instruction.loadMember("value")).setLocation(binding.loc()); - target.add(VariableNode.toSet(target, loc(), binding.name, false, true)); + target.add(VariableNode.toSet(target, loc(), binding.name, false, true)).setLocation(binding.loc()); target.setLocationAndDebug(object.loc(), BreakpointType.STEP_OVER); var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); + body.compile(target, false, BreakpointType.STEP_OVER); int endI = target.size(); 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 91825d5..7eb2852 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/ForNode.java @@ -7,7 +7,6 @@ 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.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -24,31 +23,34 @@ public class ForNode extends Node { body.resolve(target); } @Override public void compileFunctions(CompileResult target) { - declaration.compileFunctions(target); - assignment.compileFunctions(target); - condition.compileFunctions(target); + if (declaration != null) declaration.compileFunctions(target); + if (assignment != null) assignment.compileFunctions(target); + if (condition != null) condition.compileFunctions(target); body.compileFunctions(target); } @Override public void compile(CompileResult target, boolean pollute) { - declaration.compile(target, false, BreakpointType.STEP_OVER); + if (declaration != null) declaration.compile(target, false, BreakpointType.STEP_OVER); int start = target.size(); - CompoundNode.compileMultiEntry(condition, target, true, BreakpointType.STEP_OVER); - int mid = target.temp(); + int mid = -1; + if (condition != null) { + condition.compile(target, true, BreakpointType.STEP_OVER); + mid = target.temp(); + } var end = new DeferredIntSupplier(); LabelContext.pushLoop(target.env, loc(), label, end, start); - CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); + body.compile(target, false, BreakpointType.STEP_OVER); - CompoundNode.compileMultiEntry(assignment, target, false, BreakpointType.STEP_OVER); + if (assignment != null) assignment.compile(target, false, BreakpointType.STEP_OVER); int endI = target.size(); end.set(endI); LabelContext.popLoop(target.env, label); target.add(Instruction.jmp(start - endI)); - target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); + if (mid >= 0) target.set(mid, Instruction.jmpIfNot(endI - mid + 1)); if (pollute) target.add(Instruction.pushUndefined()); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java index 8564d03..d6013b9 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/IfNode.java @@ -23,7 +23,7 @@ public class IfNode extends Node { @Override public void compileFunctions(CompileResult target) { condition.compileFunctions(target); body.compileFunctions(target); - if (elseBody != null) body.compileFunctions(target); + if (elseBody != null) elseBody.compileFunctions(target); } @Override public void compile(CompileResult target, boolean pollute, BreakpointType breakpoint) { condition.compile(target, true, breakpoint); @@ -33,7 +33,7 @@ public class IfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.getBreak(target.env).push(loc(), label, end); - body.compile(target, false, BreakpointType.STEP_OVER); + body.compile(target, pollute, BreakpointType.STEP_OVER); LabelContext.getBreak(target.env).pop(label); int endI = target.size(); @@ -46,11 +46,11 @@ public class IfNode extends Node { var end = new DeferredIntSupplier(); LabelContext.getBreak(target.env).push(loc(), label, end); - body.compile(target, false, BreakpointType.STEP_OVER); + body.compile(target, pollute, BreakpointType.STEP_OVER); int mid = target.temp(); - elseBody.compile(target, false, BreakpointType.STEP_OVER); + elseBody.compile(target, pollute, BreakpointType.STEP_OVER); LabelContext.getBreak(target.env).pop(label); int endI = target.size(); 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 cce1309..f32baa2 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/SwitchNode.java @@ -62,7 +62,7 @@ public class SwitchNode extends Node { int start = target.temp(); var end = new DeferredIntSupplier(); - LabelContext.getBreak(target.env).push(loc(), label, end); + LabelContext.getBreak(target.env).pushLoop(loc(), label, end); for (var stm : body) { statementToIndex.put(statementToIndex.size(), target.size()); stm.compile(target, false, BreakpointType.STEP_OVER); @@ -70,7 +70,7 @@ public class SwitchNode extends Node { int endI = target.size(); end.set(endI); - LabelContext.getBreak(target.env).pop(label); + LabelContext.getBreak(target.env).popLoop(label); target.add(Instruction.discard()); if (pollute) target.add(Instruction.pushUndefined()); @@ -104,6 +104,7 @@ public class SwitchNode extends Node { var val = JavaScript.parseExpression(src, i + n, 0); if (!val.isSuccess()) return val.chainError(src.loc(i + n), "Expected a value after 'case'"); n += val.n; + n += Parsing.skipEmpty(src, i + n); if (!src.is(i + n, ":")) return ParseRes.error(src.loc(i + n), "Expected colons after 'case' value"); n++; 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 ffdef9e..f2dcda1 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/TryNode.java @@ -46,8 +46,8 @@ public class TryNode extends Node { if (captureName != null) { var catchVar = target.scope.defineCatch(captureName); - target.add(Instruction.loadError()); - target.add(catchVar.index().toSet(false)); + target.add(Instruction.loadError()).setLocation(catchBody.loc()); + target.add(catchVar.index().toSet(false)).setLocation(catchBody.loc()); catchBody.compile(target, false); target.scope.undefineCatch(); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java index c1ba16e..13bbc2f 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/control/WhileNode.java @@ -7,7 +7,6 @@ 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.CompileResult; -import me.topchetoeu.jscript.compilation.CompoundNode; import me.topchetoeu.jscript.compilation.DeferredIntSupplier; import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.LabelContext; @@ -33,7 +32,7 @@ public class WhileNode extends Node { LabelContext.pushLoop(target.env, loc(), label, end, start); - CompoundNode.compileMultiEntry(body, target, false, BreakpointType.STEP_OVER); + body.compile(target, false, BreakpointType.STEP_OVER); var endI = target.size(); end.set(endI + 1); diff --git a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java index 4522750..279e560 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/members/PropertyMemberNode.java @@ -42,13 +42,6 @@ public final class PropertyMemberNode extends FunctionNode implements Member { key.compile(target, true); target.add(Instruction.loadFunc(target.childrenIndices.get(this), name(name), captures(target))).setLocation(loc()); - target.add(VariableNode.toSet(target, end, name(name), false, true)); - target.add(Instruction.defProp(isSetter())); - } - - - @Override public void compile(CompileResult target, boolean pollute) { - super.compile(target, pollute); target.add(Instruction.defProp(isSetter())); } diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java index ccfeb75..8e4ba22 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/ArrayNode.java @@ -16,7 +16,9 @@ public class ArrayNode extends Node { public final Node[] statements; @Override public void compileFunctions(CompileResult target) { - for (var stm : statements) stm.compileFunctions(target); + for (var stm : statements) { + if (stm != null) stm.compileFunctions(target); + } } @Override public void compile(CompileResult target, boolean pollute) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java index 26807d4..11cab99 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/RegexNode.java @@ -31,28 +31,35 @@ public class RegexNode extends Node { var inBrackets = false; - while (true) { - if (src.is(i + n, '[')) { - n++; - inBrackets = true; - source.append(src.at(i + n)); - continue; - } - else if (src.is(i + n, ']')) { - n++; - inBrackets = false; - source.append(src.at(i + n)); - continue; - } - else if (src.is(i + n, '/') && !inBrackets) { - n++; - break; - } - - var charRes = Parsing.parseChar(src, i + n); - if (charRes.result == null) return ParseRes.error(src.loc(i + n), "Multiline regular expressions are not allowed"); - source.append(charRes.result); - n++; + loop: while (true) { + switch (src.at(i + n)) { + case '[': + inBrackets = true; + source.append('['); + n++; + continue; + case ']': + inBrackets = false; + source.append(']'); + n++; + continue; + case '/': + n++; + if (inBrackets) { + source.append('/'); + continue; + } + else break loop; + case '\\': + source.append('\\'); + source.append(src.at(i + n + 1)); + n += 2; + break; + default: + source.append(src.at(i + n)); + n++; + break; + } } while (true) { 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 39f4b99..9e76617 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/VariableNode.java @@ -23,7 +23,7 @@ public class VariableNode extends Node implements ChangeTarget { } @Override public void afterAssign(CompileResult target, boolean pollute) { - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)).setLocation(loc()); } @Override public void compile(CompileResult target, boolean pollute) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java index ba4b697..3e2315c 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/DiscardNode.java @@ -14,7 +14,7 @@ public class DiscardNode extends Node { public final Node value; @Override public void compileFunctions(CompileResult target) { - value.compileFunctions(target); + if (value != null) value.compileFunctions(target); } @Override public void compile(CompileResult target, boolean pollute) { diff --git a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java index 9be0d0e..7474a0e 100644 --- a/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java +++ b/src/main/java/me/topchetoeu/jscript/compilation/values/operations/VariableAssignNode.java @@ -22,11 +22,11 @@ public class VariableAssignNode extends Node { target.add(VariableNode.toGet(target, loc(), name)); FunctionNode.compileWithName(value, target, true, name); target.add(Instruction.operation(operation)); - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)).setLocation(loc()); } else { FunctionNode.compileWithName(value, target, true, name); - target.add(VariableNode.toSet(target, loc(), name, pollute, false)); + target.add(VariableNode.toSet(target, loc(), name, pollute, false)).setLocation(loc()); } } -- 2.45.2 From b0d8a072aabe04f8ad2dde9948a916df47216634 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:38:39 +0200 Subject: [PATCH 39/41] add hashCode to primitives --- .../jscript/runtime/values/primitives/BoolValue.java | 3 +++ .../jscript/runtime/values/primitives/StringValue.java | 3 +++ .../jscript/runtime/values/primitives/VoidValue.java | 3 ++- .../jscript/runtime/values/primitives/numbers/DoubleValue.java | 3 +++ .../jscript/runtime/values/primitives/numbers/IntValue.java | 3 +++ 5 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java index cb963e4..7fb5398 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/BoolValue.java @@ -20,6 +20,9 @@ public final class BoolValue extends PrimitiveValue { return env.get(BOOL_PROTO); } + @Override public int hashCode() { + return Boolean.hashCode(value); + } @Override public boolean equals(Object other) { if (other == this) return true; else if (other instanceof BoolValue bool) return value == bool.value; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java index 7789e06..378dab8 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/StringValue.java @@ -36,6 +36,9 @@ public final class StringValue extends PrimitiveValue { } @Override public String toString(Environment ext) { return value; } + @Override public int hashCode() { + return value.hashCode(); + } @Override public boolean equals(Object other) { if (this == other) return true; else if (other instanceof StringValue val) return value.length() == val.value.length() && value.equals(val.value); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java index 07c53d6..938db35 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/VoidValue.java @@ -23,7 +23,8 @@ public final class VoidValue extends PrimitiveValue { @Override public ObjectValue getPrototype(Environment env) { return null; } @Override public Member getOwnMember(Environment env, KeyCache key) { - throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); + if (key.isSymbol()) throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toSymbol().toString())); + else throw EngineException.ofError(String.format("Cannot read properties of %s (reading '%s')", name, key.toString(env))); } @Override public List toReadableLines(Environment env, HashSet passed) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java index 183ff49..0c75f61 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/DoubleValue.java @@ -24,6 +24,9 @@ public final class DoubleValue extends NumberValue { @Override public String toString() { return JSON.stringify(JSONElement.number(value)); } + @Override public int hashCode() { + return Double.hashCode(value); + } @Override public boolean equals(Object other) { if (this == other) return true; else if (other instanceof NumberValue val) return value == val.getDouble(); diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java index 5124c1b..b624bdb 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/numbers/IntValue.java @@ -26,6 +26,9 @@ public final class IntValue extends NumberValue { return value; } + @Override public int hashCode() { + return Long.hashCode(value); + } @Override public String toString() { return value + ""; } @Override public boolean equals(Object other) { if (this == other) return true; -- 2.45.2 From 3f5e1a5fd875ad068f1d90ca1d16c99373bcc31e Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:38:53 +0200 Subject: [PATCH 40/41] feat: implement user values --- .../runtime/values/functions/Arguments.java | 6 ++ .../runtime/values/primitives/UserValue.java | 69 +++++++++++++++++++ 2 files changed, 75 insertions(+) create mode 100644 src/main/java/me/topchetoeu/jscript/runtime/values/primitives/UserValue.java diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java index 1a7f276..7b280c4 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.primitives.UserValue; public class Arguments { public final Value self; @@ -21,6 +22,11 @@ public class Arguments { public Value self() { return get(-1); } + @SuppressWarnings("unchecked") + public T self(Class clazz) { + if (self instanceof UserValue user && clazz.isInstance(user.value)) return (T)user.value; + else return null; + } public Value get(int i) { if (i >= args.length || i < -1) return Value.UNDEFINED; else if (i == -1) return self; diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/UserValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/UserValue.java new file mode 100644 index 0000000..33985d8 --- /dev/null +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/primitives/UserValue.java @@ -0,0 +1,69 @@ +package me.topchetoeu.jscript.runtime.values.primitives; + +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Set; + +import me.topchetoeu.jscript.common.environment.Environment; +import me.topchetoeu.jscript.runtime.values.KeyCache; +import me.topchetoeu.jscript.runtime.values.Member; +import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; +import me.topchetoeu.jscript.runtime.values.primitives.numbers.NumberValue; + +public final class UserValue extends Value { + public final T value; + public final ObjectValue prototype; + + @Override public StringValue type() { return StringValue.of("object"); } + + @Override public boolean toBoolean() { return true; } + @Override public NumberValue toNumber(Environment ext) { return NumberValue.NAN; } + @Override public String toString(Environment ext) { return "[user value]"; } + + @Override public final boolean defineOwnMember(Environment env, KeyCache key, Member member) { return false; } + @Override public final boolean deleteOwnMember(Environment env, KeyCache key) { return false; } + @Override public final boolean isPrimitive() { return false; } + @Override public final Value toPrimitive(Environment env) { return NumberValue.NAN; } + + @Override public final boolean setPrototype(Environment env, ObjectValue val) { return false; } + + @Override public Member getOwnMember(Environment env, KeyCache key) { return null; } + @Override public Set getOwnMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); } + @Override public Set getOwnSymbolMembers(Environment env, boolean onlyEnumerable) { return new HashSet<>(); } + + @Override public State getState() { return State.FROZEN; } + + @Override public void preventExtensions() {} + @Override public void seal() {} + @Override public void freeze() {} + + @Override public int hashCode() { + return value.hashCode(); + } + @Override public boolean equals(Object other) { + if (this == other) return true; + else if (other instanceof UserValue val) return Objects.equals(value, val.value); + else return false; + } + + @Override public ObjectValue getPrototype(Environment env) { return prototype; } + + @Override public List toReadableLines(Environment env, HashSet passed) { + return Arrays.asList(value.toString()); + } + + private UserValue(T value, ObjectValue prototype) { + this.value = value; + this.prototype = prototype; + } + + public static UserValue of(T value) { + return new UserValue(value, null); + } + public static UserValue of(T value, ObjectValue prototype) { + return new UserValue(value, prototype); + } +} -- 2.45.2 From 65f9debecccb60218a8dcac7ff6d4add1c6305e8 Mon Sep 17 00:00:00 2001 From: TopchetoEU <36534413+TopchetoEU@users.noreply.github.com> Date: Mon, 9 Dec 2024 23:39:05 +0200 Subject: [PATCH 41/41] fix: use default construct method --- .../java/me/topchetoeu/jscript/runtime/InstructionRunner.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java index dd44deb..ec6efa7 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/InstructionRunner.java @@ -43,7 +43,7 @@ public class InstructionRunner { var callArgs = frame.take(instr.get(0)); var funcObj = frame.pop(); - frame.push(funcObj.construct(env, instr.get(1), callArgs)); + frame.push(funcObj.construct(env, callArgs)); frame.codePtr++; return null; -- 2.45.2