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) {