feat: implement derived classes
This commit is contained in:
parent
7fcb9ed19f
commit
ee78bdc1cb
@ -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);
|
||||
|
@ -90,7 +90,7 @@ public class Environment {
|
||||
if (has(key)) return get(key);
|
||||
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);
|
||||
else return defaultVal.get();
|
||||
}
|
||||
|
@ -38,6 +38,11 @@ public class ParseRes<T> {
|
||||
return new ParseRes<>(state, errorLocation, error, null, 0);
|
||||
}
|
||||
@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) {
|
||||
if (!this.isError()) return new ParseRes<>(State.ERROR, loc, error, null, 0);
|
||||
return (ParseRes<T2>) this;
|
||||
|
@ -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<Member> protoMembers;
|
||||
public final Parameters constructorParameters;
|
||||
public final CompoundNode constructorBody;
|
||||
public final Node superExpr;
|
||||
public final boolean hasConstr;
|
||||
|
||||
public ClassBody(
|
||||
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.protoFields = protoFields;
|
||||
this.protoMembers = protoMembers;
|
||||
this.constructorParameters = constructorParameters;
|
||||
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 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<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 += 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) {
|
||||
|
@ -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<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) {
|
||||
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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<? extends Node> parseParens(Source src, int i) {
|
||||
@ -130,7 +130,7 @@ public final class JavaScript {
|
||||
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);
|
||||
Node prev = null;
|
||||
|
||||
@ -174,11 +174,11 @@ public final class JavaScript {
|
||||
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);
|
||||
}
|
||||
|
||||
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);
|
||||
if (!res.isSuccess()) return res.chainError();
|
||||
|
||||
@ -219,7 +219,7 @@ public final class JavaScript {
|
||||
|
||||
public static ParseRes<Boolean> 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);
|
||||
|
@ -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();
|
||||
|
@ -118,7 +118,6 @@ public class SwitchNode extends Node {
|
||||
|
||||
return ParseRes.res(null, n);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public static ParseRes<SwitchNode> parse(Source src, int i) {
|
||||
var n = Parsing.skipEmpty(src, i);
|
||||
var loc = src.loc(i + n);
|
||||
|
@ -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) {
|
||||
|
@ -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)));
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
|
@ -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
|
||||
*
|
||||
|
@ -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<VariableIndex> indexer(Variable var) {
|
||||
return varMap.get(var);
|
||||
}
|
||||
@ -162,7 +166,6 @@ public final class VariableList {
|
||||
}
|
||||
|
||||
first = last = null;
|
||||
varMap = null;
|
||||
}
|
||||
|
||||
public Iterable<Variable> all() {
|
||||
|
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
|
@ -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<IndexNode> parseIndex(Source src, int i, Node prev, int precedence) {
|
||||
if (precedence > 18) return ParseRes.failed();
|
||||
|
||||
|
@ -28,9 +28,9 @@ public interface EventLoop {
|
||||
}
|
||||
|
||||
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) {
|
||||
return pushMsg(() -> Compiler.compileFunc(env, filename, raw).invoke(env, thisArg, args), micro);
|
||||
return pushMsg(() -> Compiler.compileFunc(env, filename, raw).apply(env, thisArg, args), micro);
|
||||
}
|
||||
}
|
||||
|
@ -15,6 +15,11 @@ import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
|
||||
public final class Frame {
|
||||
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 {
|
||||
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;
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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));
|
||||
|
||||
|
@ -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<? extends Value> 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) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user