feat: implement derived classes

This commit is contained in:
TopchetoEU 2024-09-21 18:06:03 +03:00
parent 7fcb9ed19f
commit ee78bdc1cb
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
29 changed files with 327 additions and 101 deletions

View File

@ -17,11 +17,13 @@ public class Instruction {
TRY_END(0x06), TRY_END(0x06),
CALL(0x10), CALL(0x10),
@Deprecated
CALL_MEMBER(0x11), CALL_MEMBER(0x11),
CALL_NEW(0x12), CALL_NEW(0x12),
JMP_IF(0x13), CALL_SUPER(0x13),
JMP_IFN(0x14), JMP_IF(0x18),
JMP(0x15), JMP_IFN(0x19),
JMP(0x1A),
PUSH_UNDEFINED(0x20), PUSH_UNDEFINED(0x20),
PUSH_NULL(0x21), PUSH_NULL(0x21),
@ -59,6 +61,7 @@ public class Instruction {
KEYS(0x52), KEYS(0x52),
TYPEOF(0x53), TYPEOF(0x53),
OPERATION(0x54), OPERATION(0x54),
EXTEND(0x55),
GLOB_GET(0x60), GLOB_GET(0x60),
GLOB_SET(0x61), GLOB_SET(0x61),
@ -282,15 +285,17 @@ public class Instruction {
return new Instruction(Type.NOP, params); return new Instruction(Type.NOP, params);
} }
public static Instruction call(int argn, String name) { public static Instruction call(int argn, boolean hasSelf, String name) {
return new Instruction(Type.CALL, argn, name); return new Instruction(Type.CALL, argn, hasSelf, name);
} }
public static Instruction call(int argn) { public static Instruction call(int argn, boolean hasSelf) {
return call(argn, ""); return call(argn, hasSelf, "");
} }
@Deprecated
public static Instruction callMember(int argn, String name) { public static Instruction callMember(int argn, String name) {
return new Instruction(Type.CALL_MEMBER, argn, name); return new Instruction(Type.CALL_MEMBER, argn, name);
} }
@Deprecated
public static Instruction callMember(int argn) { public static Instruction callMember(int argn) {
return new Instruction(Type.CALL_MEMBER, argn, ""); return new Instruction(Type.CALL_MEMBER, argn, "");
} }
@ -300,6 +305,9 @@ public class Instruction {
public static Instruction callNew(int argn) { public static Instruction callNew(int argn) {
return new Instruction(Type.CALL_NEW, 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) { public static Instruction jmp(int offset) {
return new Instruction(Type.JMP, offset); return new Instruction(Type.JMP, offset);
@ -321,7 +329,6 @@ public class Instruction {
return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i); return i -> new Instruction(Type.JMP_IFN, pos.getAsInt() - i);
} }
public static Instruction pushUndefined() { public static Instruction pushUndefined() {
return new Instruction(Type.PUSH_UNDEFINED); return new Instruction(Type.PUSH_UNDEFINED);
} }
@ -386,16 +393,18 @@ public class Instruction {
public static Instruction loadRegex(String pattern, String flags) { public static Instruction loadRegex(String pattern, String flags) {
return new Instruction(Type.LOAD_REGEX, pattern, 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 = ""; if (name == null) name = "";
var args = new Object[5 + captures.length]; var args = new Object[6 + captures.length];
args[0] = id; args[0] = id;
args[1] = name; args[1] = name;
args[2] = callable; args[2] = callable;
args[3] = constructible; args[3] = constructible;
args[4] = captureThis; 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); return new Instruction(Type.LOAD_FUNC, args);
} }
public static Instruction loadObj() { public static Instruction loadObj() {
@ -460,6 +469,9 @@ public class Instruction {
public static Instruction defField(boolean enumerable) { public static Instruction defField(boolean enumerable) {
return new Instruction(Type.DEF_FIELD, enumerable); return new Instruction(Type.DEF_FIELD, enumerable);
} }
public static Instruction extend() {
return new Instruction(Type.EXTEND);
}
public static Instruction operation(Operation op) { public static Instruction operation(Operation op) {
return new Instruction(Type.OPERATION, op); return new Instruction(Type.OPERATION, op);

View File

@ -90,7 +90,7 @@ public class Environment {
if (has(key)) return get(key); if (has(key)) return get(key);
else return defaultVal; else return defaultVal;
} }
public <T> T get(Key<T> key, Supplier<T> defaultVal) { public <T> T getWith(Key<T> key, Supplier<T> defaultVal) {
if (has(key)) return get(key); if (has(key)) return get(key);
else return defaultVal.get(); else return defaultVal.get();
} }

View File

@ -38,6 +38,11 @@ public class ParseRes<T> {
return new ParseRes<>(state, errorLocation, error, null, 0); return new ParseRes<>(state, errorLocation, error, null, 0);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(ParseRes<?> other) {
if (!this.isError()) return other.chainError();
return (ParseRes<T2>) this;
}
@SuppressWarnings("unchecked")
public <T2> ParseRes<T2> chainError(Location loc, String error) { public <T2> ParseRes<T2> chainError(Location loc, String error) {
if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0); if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0);
return (ParseRes<T2>) this; return (ParseRes<T2>) this;

View File

@ -3,9 +3,13 @@ package me.topchetoeu.jscript.compilation;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.List; import java.util.List;
import java.util.function.Consumer;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.SyntaxException;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.environment.Key;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
@ -14,6 +18,7 @@ import me.topchetoeu.jscript.compilation.members.FieldMemberNode;
import me.topchetoeu.jscript.compilation.members.Member; import me.topchetoeu.jscript.compilation.members.Member;
import me.topchetoeu.jscript.compilation.members.MethodMemberNode; import me.topchetoeu.jscript.compilation.members.MethodMemberNode;
import me.topchetoeu.jscript.compilation.members.PropertyMemberNode; import me.topchetoeu.jscript.compilation.members.PropertyMemberNode;
import me.topchetoeu.jscript.compilation.scope.FunctionScope;
public abstract class ClassNode extends FunctionNode { public abstract class ClassNode extends FunctionNode {
public static final class ClassBody { public static final class ClassBody {
@ -22,20 +27,29 @@ public abstract class ClassNode extends FunctionNode {
public final List<Member> protoMembers; public final List<Member> protoMembers;
public final Parameters constructorParameters; public final Parameters constructorParameters;
public final CompoundNode constructorBody; public final CompoundNode constructorBody;
public final Node superExpr;
public final boolean hasConstr;
public ClassBody( public ClassBody(
List<Member> staticMembers, List<FieldMemberNode> protoFields, List<Member> protoMembers, List<Member> staticMembers, List<FieldMemberNode> protoFields, List<Member> protoMembers,
Parameters constructorParameters, CompoundNode constructorBody Parameters constructorParameters, CompoundNode constructorBody,
Node superExpr, boolean hasConstr
) { ) {
this.staticMembers = staticMembers; this.staticMembers = staticMembers;
this.protoFields = protoFields; this.protoFields = protoFields;
this.protoMembers = protoMembers; this.protoMembers = protoMembers;
this.constructorParameters = constructorParameters; this.constructorParameters = constructorParameters;
this.constructorBody = constructorBody; this.constructorBody = constructorBody;
this.superExpr = superExpr;
this.hasConstr = hasConstr;
} }
} }
// public static final Key<Void> public static final Key<Environment> CLASS_ROOT = Key.of();
public static final Key<Consumer<CompileResult>> SUPER = Key.of();
public static final Key<Consumer<CompileResult>> SUPER_PROTO = Key.of();
public static final Key<Consumer<CompileResult>> SUPER_CONSTR = Key.of();
public static final Key<Consumer<CompileResult>> ON_SUPER_CALL = Key.of();
public final ClassBody body; public final ClassBody body;
public final String name; public final String name;
@ -43,7 +57,9 @@ public abstract class ClassNode extends FunctionNode {
@Override public String name() { return name; } @Override public String name() { return name; }
public void compileStatic(CompileResult target) { 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) { public void compilePrototype(CompileResult target) {
if (body.protoMembers.size() > 0) { 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) { for (var member : body.protoFields) {
target.add(Instruction.loadThis()); target.add(Instruction.loadThis());
member.compile(target, false, true); 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) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null)); if (body.superExpr == null) {
target.add(_i -> Instruction.loadFunc(id, false, true, false, name, captures(id, target))); var id = target.addChild(compileBody(target, name, null));
compileStatic(target); target.add(_i -> Instruction.loadFunc(id, false, true, false, false, name, captures(id, target)));
compilePrototype(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) { 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 n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n); var loc = src.loc(i + n);
if (!src.is(i + n, "{")) return ParseRes.failed(); ParseRes<Node> 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++;
n += Parsing.skipEmpty(src, i + n); n += Parsing.skipEmpty(src, i + n);
@ -105,7 +185,7 @@ public abstract class ClassNode extends FunctionNode {
if (src.is(i + n, "}")) { if (src.is(i + n, "}")) {
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) { 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."); 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) { // public FunctionStatementNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {

View File

@ -10,6 +10,7 @@ import me.topchetoeu.jscript.common.FunctionBody;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment; 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;
import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder; import me.topchetoeu.jscript.common.mapping.FunctionMap.FunctionMapBuilder;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
@ -142,7 +143,21 @@ public final class CompileResult {
} }
public CompileResult subtarget() { 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<Environment> 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<CompileResult> task) { public CompileResult(Environment env, Scope scope, int length, Consumer<CompileResult> task) {
@ -154,11 +169,11 @@ public final class CompileResult {
this.length = length; this.length = length;
this.buildTask = () -> task.accept(this); this.buildTask = () -> task.accept(this);
} }
private CompileResult(Scope scope, CompileResult parent) { private CompileResult(Environment env, Scope scope, CompileResult parent) {
this.scope = scope; this.scope = scope;
this.instructions = parent.instructions; this.instructions = parent.instructions;
this.children = parent.children; this.children = parent.children;
this.map = parent.map; this.map = parent.map;
this.env = parent.env; this.env = env;
} }
} }

View File

@ -4,6 +4,7 @@ import java.util.Arrays;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
@ -16,7 +17,11 @@ public class FunctionArrowNode extends FunctionNode {
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null)); 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) { public FunctionArrowNode(Location loc, Location end, Parameters params, Node body) {

View File

@ -24,6 +24,10 @@ public abstract class FunctionNode extends Node {
protected void compilePreBody(CompileResult target) { } 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) { public final CompileResult compileBody(Environment env, FunctionScope scope, boolean lastReturn, String _name, String selfName) {
var name = this.name() != null ? this.name() : _name; 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) { 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); public abstract void compile(CompileResult target, boolean pollute, String name, BreakpointType bp);

View File

@ -17,7 +17,7 @@ public class FunctionStatementNode extends FunctionNode {
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null)); 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)); target.add(VariableNode.toInit(target, end, this.name));
if (pollute) target.add(Instruction.pushUndefined()); if (pollute) target.add(Instruction.pushUndefined());
} }

View File

@ -11,7 +11,7 @@ public class FunctionValueNode extends FunctionNode {
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
var id = target.addChild(compileBody(target, name, null)); 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) { public FunctionValueNode(Location loc, Location end, Parameters params, CompoundNode body, String name) {

View File

@ -68,7 +68,7 @@ public final class JavaScript {
"finally", "for", "do", "while", "switch", "case", "default", "new", "finally", "for", "do", "while", "switch", "case", "default", "new",
"function", "var", "return", "throw", "typeof", "delete", "break", "function", "var", "return", "throw", "typeof", "delete", "break",
"continue", "debugger", "implements", "interface", "package", "private", "continue", "debugger", "implements", "interface", "package", "private",
"protected", "public", "static", "arguments", "class" "protected", "public", "static", "arguments", "class", "extends"
)); ));
public static ParseRes<? extends Node> parseParens(Source src, int i) { public static ParseRes<? extends Node> parseParens(Source src, int i) {
@ -130,7 +130,7 @@ public final class JavaScript {
return ParseRes.failed(); return ParseRes.failed();
} }
public static ParseRes<? extends Node> parseExpression(Source src, int i, int precedence, boolean statement) { public static ParseRes<Node> parseExpression(Source src, int i, int precedence, boolean statement) {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);
Node prev = null; Node prev = null;
@ -174,11 +174,11 @@ public final class JavaScript {
else return ParseRes.res(prev, n); else return ParseRes.res(prev, n);
} }
public static ParseRes<? extends Node> parseExpression(Source src, int i, int precedence) { public static ParseRes<Node> parseExpression(Source src, int i, int precedence) {
return parseExpression(src, i, precedence, false); return parseExpression(src, i, precedence, false);
} }
public static ParseRes<? extends Node> parseExpressionStatement(Source src, int i) { public static ParseRes<Node> parseExpressionStatement(Source src, int i) {
var res = parseExpression(src, i, 0, true); var res = parseExpression(src, i, 0, true);
if (!res.isSuccess()) return res.chainError(); if (!res.isSuccess()) return res.chainError();
@ -219,7 +219,7 @@ public final class JavaScript {
public static ParseRes<Boolean> parseStatementEnd(Source src, int i) { public static ParseRes<Boolean> parseStatementEnd(Source src, int i) {
var n = Parsing.skipEmpty(src, 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++) { for (var j = i; j < i + n; j++) {
if (src.is(j, '\n')) return ParseRes.res(true, n); if (src.is(j, '\n')) return ParseRes.res(true, n);

View File

@ -31,13 +31,12 @@ public class ForOfNode extends Node {
target.add(Instruction.dup()); target.add(Instruction.dup());
target.add(Instruction.loadIntrinsics("it_key")); target.add(Instruction.loadIntrinsics("it_key"));
target.add(Instruction.loadMember()).setLocation(iterable.loc()); 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(); int start = target.size();
target.add(Instruction.dup()); target.add(Instruction.dup(2, 0));
target.add(Instruction.dup());
target.add(Instruction.loadMember("next")).setLocation(iterable.loc()); 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.dup());
target.add(Instruction.loadMember("done")).setLocation(iterable.loc()); target.add(Instruction.loadMember("done")).setLocation(iterable.loc());
int mid = target.temp(); int mid = target.temp();

View File

@ -118,7 +118,6 @@ public class SwitchNode extends Node {
return ParseRes.res(null, n); return ParseRes.res(null, n);
} }
@SuppressWarnings("unused")
public static ParseRes<SwitchNode> parse(Source src, int i) { public static ParseRes<SwitchNode> parse(Source src, int i) {
var n = Parsing.skipEmpty(src, i); var n = Parsing.skipEmpty(src, i);
var loc = src.loc(i + n); var loc = src.loc(i + n);

View File

@ -2,6 +2,7 @@ package me.topchetoeu.jscript.compilation.members;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
@ -22,12 +23,16 @@ public class MethodMemberNode extends FunctionNode implements Member {
else return null; else return null;
} }
@Override protected Environment rootEnv(Environment env) {
return env;
}
@Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) { @Override public void compile(CompileResult target, boolean pollute, String name, BreakpointType bp) {
if (pollute) target.add(Instruction.dup()); if (pollute) target.add(Instruction.dup());
key.compile(target, true); key.compile(target, true);
var id = target.addChild(compileBody(target, name, null)); 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) { @Override public void compile(CompileResult target, boolean pollute, boolean enumerable) {

View File

@ -4,6 +4,7 @@ import java.util.Arrays;
import me.topchetoeu.jscript.common.Instruction; import me.topchetoeu.jscript.common.Instruction;
import me.topchetoeu.jscript.common.Instruction.BreakpointType; import me.topchetoeu.jscript.common.Instruction.BreakpointType;
import me.topchetoeu.jscript.common.environment.Environment;
import me.topchetoeu.jscript.common.parsing.Location; import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
@ -29,6 +30,10 @@ public class PropertyMemberNode extends FunctionNode implements Member{
else return null; else return null;
} }
@Override protected Environment rootEnv(Environment env) {
return env;
}
public boolean isGetter() { return argument == null; } public boolean isGetter() { return argument == null; }
public boolean isSetter() { return argument != null; } public boolean isSetter() { return argument != null; }
@ -37,7 +42,7 @@ public class PropertyMemberNode extends FunctionNode implements Member{
key.compile(target, true); key.compile(target, true);
var id = target.addChild(compileBody(target, name, null)); 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)));
} }

View File

@ -67,6 +67,21 @@ public class FunctionScope extends Scope {
return childVar; 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) { @Override public boolean has(String name, boolean capture) {
if (functionVarMap.containsKey(name)) return true; if (functionVarMap.containsKey(name)) return true;
if (specialVarMap.containsKey(name)) return true; if (specialVarMap.containsKey(name)) return true;

View File

@ -111,6 +111,18 @@ public class Scope {
return null; 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 * Checks if the given variable name is accessible
* *

View File

@ -64,7 +64,7 @@ public final class VariableList {
public boolean frozen() { public boolean frozen() {
if (frozenList != null) { if (frozenList != null) {
assert frozenList != null; assert frozenList != null;
assert varMap == null; assert varMap != null;
assert first == null; assert first == null;
assert last == null; assert last == null;
@ -139,6 +139,10 @@ public final class VariableList {
return node.var; return node.var;
} }
public boolean has(Variable var) {
return varMap.containsKey(var);
}
public Supplier<VariableIndex> indexer(Variable var) { public Supplier<VariableIndex> indexer(Variable var) {
return varMap.get(var); return varMap.get(var);
} }
@ -162,7 +166,6 @@ public final class VariableList {
} }
first = last = null; first = last = null;
varMap = null;
} }
public Iterable<Variable> all() { public Iterable<Variable> all() {

View File

@ -5,6 +5,7 @@ import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.ClassNode; import me.topchetoeu.jscript.compilation.ClassNode;
import me.topchetoeu.jscript.compilation.JavaScript;
public class ClassValueNode extends ClassNode { public class ClassValueNode extends ClassNode {
public ClassValueNode(Location loc, Location end, String name, ClassBody body) { public ClassValueNode(Location loc, Location end, String name, ClassBody body) {
@ -19,10 +20,13 @@ public class ClassValueNode extends ClassNode {
n += 5; n += 5;
var name = Parsing.parseIdentifier(src, i + n); 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; n += name.n;
var body = parseBody(src, i + 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; n += body.n;
return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n); return ParseRes.res(new ClassValueNode(loc, src.loc(i + n), name.result, body.result), n);

View File

@ -10,12 +10,14 @@ import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.ClassNode;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.values.ArgumentsNode; import me.topchetoeu.jscript.compilation.values.ArgumentsNode;
import me.topchetoeu.jscript.compilation.values.ArrayNode; import me.topchetoeu.jscript.compilation.values.ArrayNode;
import me.topchetoeu.jscript.compilation.values.ObjectNode; import me.topchetoeu.jscript.compilation.values.ObjectNode;
import me.topchetoeu.jscript.compilation.values.SuperNode;
import me.topchetoeu.jscript.compilation.values.ThisNode; import me.topchetoeu.jscript.compilation.values.ThisNode;
import me.topchetoeu.jscript.compilation.values.VariableNode; import me.topchetoeu.jscript.compilation.values.VariableNode;
import me.topchetoeu.jscript.compilation.values.constants.BoolNode; 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) { @Override public void compile(CompileResult target, boolean pollute, BreakpointType type) {
if (!isNew && func instanceof IndexNode) { var superInstr = target.env.get(ClassNode.SUPER);
var obj = ((IndexNode)func).object;
var index = ((IndexNode)func).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 = ""; String name = "";
obj.compile(target, true); if (superInstr != null && obj instanceof SuperNode) {
index.compile(target, true); 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); for (var arg : args) arg.compile(target, true);
if (ATTACH_NAME) name = generateName(obj, index); 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 { else {
String name = ""; String name = "";
@ -110,7 +131,7 @@ public class CallNode extends Node {
if (ATTACH_NAME) name = generateName(func, null); if (ATTACH_NAME) name = generateName(func, null);
if (isNew) target.add(Instruction.callNew(args.length, name)).setLocationAndDebug(loc(), type); 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()); if (!pollute) target.add(Instruction.discard());
} }

View File

@ -6,10 +6,12 @@ import me.topchetoeu.jscript.common.parsing.Location;
import me.topchetoeu.jscript.common.parsing.ParseRes; import me.topchetoeu.jscript.common.parsing.ParseRes;
import me.topchetoeu.jscript.common.parsing.Parsing; import me.topchetoeu.jscript.common.parsing.Parsing;
import me.topchetoeu.jscript.common.parsing.Source; import me.topchetoeu.jscript.common.parsing.Source;
import me.topchetoeu.jscript.compilation.ClassNode;
import me.topchetoeu.jscript.compilation.CompileResult; import me.topchetoeu.jscript.compilation.CompileResult;
import me.topchetoeu.jscript.compilation.JavaScript; import me.topchetoeu.jscript.compilation.JavaScript;
import me.topchetoeu.jscript.compilation.Node; import me.topchetoeu.jscript.compilation.Node;
import me.topchetoeu.jscript.compilation.patterns.ChangeTarget; 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.NumberNode;
import me.topchetoeu.jscript.compilation.values.constants.StringNode; import me.topchetoeu.jscript.compilation.values.constants.StringNode;
@ -18,12 +20,11 @@ public class IndexNode extends Node implements ChangeTarget {
public final Node index; public final Node index;
@Override public void beforeAssign(CompileResult target) { @Override public void beforeAssign(CompileResult target) {
object.compile(target, true); compileObj(target, object);
indexStorePushKey(target, index); indexStorePushKey(target, index);
} }
@Override public void beforeChange(CompileResult target) { @Override public void beforeChange(CompileResult target) {
object.compile(target, true); compileObj(target, object);
if (index instanceof NumberNode num && (int)num.value == num.value) { if (index instanceof NumberNode num && (int)num.value == num.value) {
target.add(Instruction.dup()); target.add(Instruction.dup());
@ -43,7 +44,7 @@ public class IndexNode extends Node implements ChangeTarget {
} }
@Override public void assign(CompileResult target, boolean pollute) { @Override public void assign(CompileResult target, boolean pollute) {
object.compile(target, true); compileObj(target, object);
target.add(Instruction.dup(1, 1)); target.add(Instruction.dup(1, 1));
indexStorePushKey(target, index); indexStorePushKey(target, index);
indexStore(target, index, pollute); indexStore(target, index, pollute);
@ -54,7 +55,7 @@ public class IndexNode extends Node implements ChangeTarget {
} }
public void compile(CompileResult target, boolean dupObj, boolean pollute) { public void compile(CompileResult target, boolean dupObj, boolean pollute) {
object.compile(target, true); compileObj(target, object);
if (dupObj) target.add(Instruction.dup()); if (dupObj) target.add(Instruction.dup());
if (index instanceof NumberNode num && (int)num.value == num.value) { if (index instanceof NumberNode num && (int)num.value == num.value) {
@ -82,6 +83,13 @@ public class IndexNode extends Node implements ChangeTarget {
this.index = index; 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<IndexNode> parseIndex(Source src, int i, Node prev, int precedence) { public static ParseRes<IndexNode> parseIndex(Source src, int i, Node prev, int precedence) {
if (precedence > 18) return ParseRes.failed(); if (precedence > 18) return ParseRes.failed();

View File

@ -28,9 +28,9 @@ public interface EventLoop {
} }
public default Future<Value> pushMsg(boolean micro, Environment env, FunctionValue func, Value thisArg, Value ...args) { public default Future<Value> 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<Value> pushMsg(boolean micro, Environment env, Filename filename, String raw, Value thisArg, Value ...args) { public default Future<Value> 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);
} }
} }

View File

@ -15,6 +15,11 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
public final class Frame { public final class Frame {
public static final Key<Frame> KEY = Key.of(); public static final Key<Frame> KEY = Key.of();
public static final EngineException STACK_OVERFLOW;
static {
STACK_OVERFLOW = EngineException.ofRange("Stack overflow!");
STACK_OVERFLOW.value.freeze();
}
public static enum TryState { public static enum TryState {
TRY, TRY,
@ -193,6 +198,7 @@ public final class Frame {
} }
} }
} }
catch (StackOverflowError e) { throw STACK_OVERFLOW; }
catch (EngineException e) { error = e; } catch (EngineException e) { error = e; }
catch (RuntimeException e) { catch (RuntimeException e) {
System.out.println(dbg.getMapOrEmpty(function).toLocation(codePtr, true)); 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) { if (error != null) {
var caught = false; var caught = false;
@ -280,10 +293,6 @@ public final class Frame {
dbg.onInstruction(env, this, instr, null, error, caught); dbg.onInstruction(env, this, instr, null, error, caught);
throw error; throw error;
} }
if (returnValue != null) {
dbg.onInstruction(env, this, instr, returnValue, null, false);
return returnValue;
}
return null; return null;
} }

View File

@ -32,18 +32,22 @@ public class InstructionRunner {
private static Value execCall(Environment env, Instruction instr, Frame frame) { private static Value execCall(Environment env, Instruction instr, Frame frame) {
var callArgs = frame.take(instr.get(0)); var callArgs = frame.take(instr.get(0));
var func = frame.pop(); 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++; frame.codePtr++;
return null; 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 callArgs = frame.take(instr.get(0));
var index = frame.pop(); var superFunc = frame.pop();
var obj = 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++; frame.codePtr++;
return null; return null;
@ -185,11 +189,12 @@ public class InstructionRunner {
boolean callable = instr.get(2); boolean callable = instr.get(2);
boolean constructible = instr.get(3); boolean constructible = instr.get(3);
boolean captureThis = instr.get(4); boolean captureThis = instr.get(4);
boolean noThis = instr.get(5);
var captures = new Value[instr.params.length - 5][];
for (var i = 5; i < instr.params.length; i++) { var captures = new Value[instr.params.length - 6][];
captures[i - 5] = frame.captureVar(instr.get(i));
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); var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
@ -199,6 +204,8 @@ public class InstructionRunner {
func.self = frame.self; func.self = frame.self;
func.argsVal = frame.argsVal; func.argsVal = frame.argsVal;
} }
if (noThis) func.mustCallSuper = true;
frame.push(func); frame.push(func);
frame.codePtr++; frame.codePtr++;
@ -487,6 +494,20 @@ public class InstructionRunner {
frame.codePtr++; frame.codePtr++;
return null; 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) { private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) {
if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal); if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal);
@ -510,6 +531,7 @@ public class InstructionRunner {
return null; return null;
} }
private static Value execLoadThis(Environment env, Instruction instr, Frame frame) { 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.push(frame.self);
frame.codePtr++; frame.codePtr++;
return null; return null;
@ -547,7 +569,7 @@ public class InstructionRunner {
case THROW_SYNTAX: return execThrowSyntax(env, instr, frame); case THROW_SYNTAX: return execThrowSyntax(env, instr, frame);
case CALL: return execCall(env, instr, frame); case CALL: return execCall(env, instr, frame);
case CALL_NEW: return execCallNew(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_START: return execTryStart(env, instr, frame);
case TRY_END: return execTryEnd(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_DEF: return exexGlobDef(env, instr, frame);
case GLOB_GET: return exexGlobGet(env, instr, frame); case GLOB_GET: return exexGlobGet(env, instr, frame);
case GLOB_SET: return exexGlobSet(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_INIT: return execVarInit(env, instr, frame);
case VAR_FREE: return execVarFree(env, instr, frame); case VAR_FREE: return execVarFree(env, instr, frame);

View File

@ -231,7 +231,7 @@ public class SimpleRepl {
var funcArgs = (ArrayValue)args.get(2); var funcArgs = (ArrayValue)args.get(2);
var name = args.get(3).toString(env); 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 -> { res.defineOwnMember(env, "construct", new NativeFunction(args -> {
var func = (FunctionValue)args.get(0); var func = (FunctionValue)args.get(0);
@ -263,7 +263,7 @@ public class SimpleRepl {
var self = args.get(1); var self = args.get(1);
var funcArgs = (ArrayValue)args.get(2); var funcArgs = (ArrayValue)args.get(2);
return func.invoke(env, self, funcArgs.toArray()); return func.apply(env, self, funcArgs.toArray());
})); }));
return res; return res;
@ -362,7 +362,7 @@ public class SimpleRepl {
glob.defineOwnMember(null, "measure", new NativeFunction("measure", args -> { glob.defineOwnMember(null, "measure", new NativeFunction("measure", args -> {
var start = System.nanoTime(); 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)); System.out.println(String.format("Finished in %sns", System.nanoTime() - start));

View File

@ -89,7 +89,7 @@ public abstract class Value {
else throw EngineException.ofType(name + " is not a function"); 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); return call(env, false, name, self, args);
} }
public final Value construct(Environment env, String name, Value ...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")); var proto = getMember(env, StringValue.of("prototype"));
if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto);
else res.setPrototype(env, null);
var ret = this.call(env, true, name, res, args); var ret = this.call(env, true, name, res, args);
if (!ret.isPrimitive()) return ret; if (!ret.isPrimitive()) return ret;
return res; 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) { public final Value apply(Environment env, Value self, Value ...args) {
return invoke(env, "", self, args); return apply(env, "", self, args);
} }
public final Value construct(Environment env, Value ...args) { public final Value construct(Environment env, Value ...args) {
return construct(env, "", args); return construct(env, "", args);
@ -118,7 +121,7 @@ public abstract class Value {
public abstract boolean toBoolean(); public abstract boolean toBoolean();
public final boolean isInstanceOf(Environment env, Value proto) { 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; if (val.equals(proto)) return true;
} }
@ -390,7 +393,7 @@ public abstract class Value {
private void loadNext() { private void loadNext() {
if (supplier == null) value = null; if (supplier == null) value = null;
else if (consumed) { 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 == null) { supplier = null; value = null; }
if (curr.getMember(env, StringValue.of("done")).toBoolean()) { 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<? extends Value> it) { public void callWith(Environment env, Iterable<? extends Value> it) {
for (var el : it) { for (var el : it) {
this.invoke(env, Value.UNDEFINED, el); this.apply(env, Value.UNDEFINED, el);
} }
} }
public void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) { public void callWithAsync(Environment env, Iterable<? extends Value> it, boolean async) {
for (var el : it) { 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);
} }
} }

View File

@ -8,11 +8,13 @@ import me.topchetoeu.jscript.runtime.values.Value;
public final class CodeFunction extends FunctionValue { public final class CodeFunction extends FunctionValue {
public final FunctionBody body; public final FunctionBody body;
public final Value[][] captures; public final Value[][] captures;
public Value self;
public Value argsVal;
public Environment env; public Environment env;
public Value self;
public Value argsVal;
private Value onCall(Frame frame) { private Value onCall(Frame frame) {
if (mustCallSuper) frame.self = null;
frame.onPush(); frame.onPush();
try { try {
@ -31,7 +33,10 @@ public final class CodeFunction extends FunctionValue {
if (argsVal != null) frame.fakeArgs = argsVal; if (argsVal != null) frame.fakeArgs = argsVal;
if (self != null) frame.self = self; 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) { public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) {

View File

@ -23,6 +23,7 @@ public abstract class FunctionValue extends ObjectValue {
public boolean enableCall = true; public boolean enableCall = true;
public boolean enableNew = true; public boolean enableNew = true;
public boolean mustCallSuper = false;
private final FieldMember nameField = new FieldMember(this, true, false, false) { private final FieldMember nameField = new FieldMember(this, true, false, false) {
@Override public Value get(Environment env, Value self) { @Override public Value get(Environment env, Value self) {

View File

@ -46,13 +46,13 @@ public class ObjectValue extends Value {
var valueOf = getMember(env, "valueOf"); var valueOf = getMember(env, "valueOf");
if (valueOf instanceof FunctionValue) { if (valueOf instanceof FunctionValue) {
var res = valueOf.invoke(env, this); var res = valueOf.apply(env, this);
if (res.isPrimitive()) return res; if (res.isPrimitive()) return res;
} }
var toString = getMember(env, "toString"); var toString = getMember(env, "toString");
if (toString instanceof FunctionValue) { if (toString instanceof FunctionValue) {
var res = toString.invoke(env, this); var res = toString.apply(env, this);
if (res.isPrimitive()) return res; if (res.isPrimitive()) return res;
} }
} }

View File

@ -241,7 +241,6 @@ target.Boolean = Boolean;
class Object { class Object {
toString() { toString() {
print("2");
if (this !== null && this !== undefined && (Symbol.toStringTag in this)) return "[object " + this[Symbol.toStringTag] + "]"; 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 === "number" || this instanceof Number) return "[object Number]";
else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]"; else if (typeof this === "symbol" || this instanceof Symbol) return "[object Symbol]";
@ -251,13 +250,11 @@ class Object {
else return "[object Object]"; else return "[object Object]";
} }
valueOf() { valueOf() {
print("1");
return this; return this;
} }
constructor(value) { constructor(value) {
if (typeof value === 'object' && value !== null) return value; if (typeof value === 'object' && value !== null) return value;
if (typeof value === 'string') return new String(value); if (typeof value === 'string') return new String(value);
if (typeof value === 'number') return new Number(value); if (typeof value === 'number') return new Number(value);
if (typeof value === 'boolean') return new Boolean(value); if (typeof value === 'boolean') return new Boolean(value);
@ -268,19 +265,15 @@ class Object {
return res; return res;
} }
const target = this; return {};
// TODO: use new.target.prototype as proto // // TODO: use new.target.prototype as proto
if (target == null || typeof target !== 'object') target = {}; // if (target == null || typeof target !== 'object') target = {};
this[valueKey] = Object(value); // return target;
} }
static defineProperty(obj, key, desc) { static defineProperty(obj, key, desc) {
if (typeof obj !== "object" || obj === null) { if (typeof obj !== "object" || obj === null) throw new TypeError("Object.defineProperty called on non-object");
print(obj);
print(typeof obj);
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 (typeof desc !== "object" || desc === null) throw new TypeError("Property description must be an object: " + desc);
if ("get" in desc || "set" in desc) { if ("get" in desc || "set" in desc) {