implement all changes in runtime
This commit is contained in:
parent
b4e7a42975
commit
41bb27e4dd
@ -1,5 +1,6 @@
|
||||
package me.topchetoeu.jscript.runtime;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Stack;
|
||||
import java.util.concurrent.CancellationException;
|
||||
|
||||
@ -12,6 +13,7 @@ import me.topchetoeu.jscript.runtime.values.Value;
|
||||
import me.topchetoeu.jscript.runtime.values.functions.CodeFunction;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ArrayLikeValue;
|
||||
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||
import me.topchetoeu.jscript.runtime.values.primitives.numbers.IntValue;
|
||||
|
||||
public final class Frame {
|
||||
public static final Key<Frame> KEY = Key.of();
|
||||
@ -95,16 +97,25 @@ public final class Frame {
|
||||
}
|
||||
|
||||
/**
|
||||
* A list of one-element arrays of values. This is so that we can pass captures to other functions
|
||||
* An array of captures from the parent function
|
||||
*/
|
||||
public final Value[][] captures;
|
||||
/**
|
||||
* An array of non-capture variables
|
||||
*/
|
||||
public final Value[] locals;
|
||||
/**
|
||||
* An array of children-captured variables
|
||||
*/
|
||||
public final Value[][] capturables;
|
||||
public final Value argsVal;
|
||||
public Value self;
|
||||
public Value fakeArgs;
|
||||
|
||||
public final Value self;
|
||||
public final Value[] args;
|
||||
public final Value argsVal;
|
||||
public final Value argsLen;
|
||||
|
||||
public final boolean isNew;
|
||||
|
||||
public final Stack<TryCtx> tryStack = new Stack<>();
|
||||
public final CodeFunction function;
|
||||
public final Environment env;
|
||||
@ -275,12 +286,9 @@ public final class Frame {
|
||||
}
|
||||
|
||||
if (returnValue != null) {
|
||||
if (self == null) error = EngineException.ofError("Super constructor must be called before returning");
|
||||
else {
|
||||
dbg.onInstruction(env, this, instr, returnValue, null, false);
|
||||
return returnValue;
|
||||
}
|
||||
}
|
||||
if (error != null) {
|
||||
var caught = false;
|
||||
|
||||
@ -358,18 +366,21 @@ public final class Frame {
|
||||
};
|
||||
}
|
||||
|
||||
public Frame(Environment env, boolean isNew, Value thisArg, Value[] args, CodeFunction func) {
|
||||
public Frame(Environment env, boolean isNew, Value self, Value[] args, CodeFunction func) {
|
||||
this.env = env;
|
||||
this.dbg = DebugContext.get(env);
|
||||
this.function = func;
|
||||
this.isNew = isNew;
|
||||
|
||||
this.self = thisArg;
|
||||
this.self = self;
|
||||
this.args = args;
|
||||
this.argsVal = new ArgumentsValue(this, args);
|
||||
this.argsLen = new IntValue(args.length);
|
||||
this.captures = func.captures;
|
||||
|
||||
this.locals = new Value[func.body.localsN];
|
||||
Arrays.fill(this.locals, Value.UNDEFINED);
|
||||
this.capturables = new Value[func.body.capturablesN][1];
|
||||
for (var i = 0; i < this.capturables.length; i++) this.capturables[i][0] = Value.UNDEFINED;
|
||||
}
|
||||
}
|
||||
|
@ -34,23 +34,7 @@ public class InstructionRunner {
|
||||
var func = frame.pop();
|
||||
var self = (boolean)instr.get(1) ? frame.pop() : Value.UNDEFINED;
|
||||
|
||||
frame.push(func.apply(env, instr.get(2), self, callArgs));
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execCallSuper(Environment env, Instruction instr, Frame frame) {
|
||||
if (!frame.isNew) throw EngineException.ofError("Super constructor may be called only when constructing");
|
||||
if (frame.self != null) throw EngineException.ofError("Super constructor may be called once");
|
||||
|
||||
var callArgs = frame.take(instr.get(0));
|
||||
var superFunc = frame.pop();
|
||||
|
||||
var self = new ObjectValue();
|
||||
if (frame.function.prototype instanceof ObjectValue objProto) self.setPrototype(env, objProto);
|
||||
|
||||
frame.self = superFunc.construct(env, "super", self, callArgs);
|
||||
frame.push(frame.self);
|
||||
frame.push(func.apply(env, self, callArgs));
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -74,10 +58,10 @@ public class InstructionRunner {
|
||||
|
||||
if (val == Value.UNDEFINED) accessor = null;
|
||||
else if (val instanceof FunctionValue func) accessor = func;
|
||||
else throw EngineException.ofType("Getter must be a function or undefined.");
|
||||
else throw EngineException.ofType("Getter must be a function or undefined");
|
||||
|
||||
if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, instr.get(1)));
|
||||
else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, instr.get(1)));
|
||||
if ((boolean)instr.get(0)) obj.defineOwnMember(env, key, new PropertyMember(obj, null, accessor, true, true));
|
||||
else obj.defineOwnMember(env, key, new PropertyMember(obj, accessor, null, true, true));
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -87,7 +71,7 @@ public class InstructionRunner {
|
||||
var key = frame.pop();
|
||||
var obj = frame.pop();
|
||||
|
||||
obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, instr.get(0), true));
|
||||
obj.defineOwnMember(env, key, FieldMember.of(obj, val, true, true, true));
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -155,9 +139,7 @@ public class InstructionRunner {
|
||||
private static Value execLoadVar(Environment env, Instruction instr, Frame frame) {
|
||||
int i = instr.get(0);
|
||||
|
||||
var res = frame.getVar(i);
|
||||
if (res == null) throw EngineException.ofSyntax("Uninitialized variable");
|
||||
frame.push(res);
|
||||
frame.push(frame.getVar(i));
|
||||
frame.codePtr++;
|
||||
|
||||
return null;
|
||||
@ -189,25 +171,14 @@ public class InstructionRunner {
|
||||
private static Value execLoadFunc(Environment env, Instruction instr, Frame frame) {
|
||||
int id = instr.get(0);
|
||||
String name = instr.get(1);
|
||||
boolean callable = instr.get(2);
|
||||
boolean constructible = instr.get(3);
|
||||
boolean captureThis = instr.get(4);
|
||||
boolean noThis = instr.get(5);
|
||||
|
||||
var captures = new Value[instr.params.length - 6][];
|
||||
var captures = new Value[instr.params.length - 2][];
|
||||
|
||||
for (var i = 6; i < instr.params.length; i++) {
|
||||
captures[i - 6] = frame.captureVar(instr.get(i));
|
||||
for (var i = 2; i < instr.params.length; i++) {
|
||||
captures[i - 2] = frame.captureVar(instr.get(i));
|
||||
}
|
||||
|
||||
var func = new CodeFunction(env, name, frame.function.body.children[id], captures);
|
||||
if (!callable) func.enableCall = false;
|
||||
if (!constructible) func.enableNew = false;
|
||||
if (captureThis) {
|
||||
func.self = frame.self;
|
||||
func.argsVal = frame.argsVal;
|
||||
}
|
||||
if (noThis) func.mustCallSuper = true;
|
||||
|
||||
frame.push(func);
|
||||
|
||||
@ -254,7 +225,7 @@ public class InstructionRunner {
|
||||
frame.push(env.get(Value.REGEX_CONSTR).construct(env, instr.get(0), instr.get(1)));
|
||||
}
|
||||
else {
|
||||
throw EngineException.ofSyntax("Regex is not supported.");
|
||||
throw EngineException.ofSyntax("Regex is not supported");
|
||||
}
|
||||
|
||||
frame.codePtr++;
|
||||
@ -271,7 +242,7 @@ public class InstructionRunner {
|
||||
var key = frame.pop();
|
||||
var obj = frame.pop();
|
||||
|
||||
if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'.");
|
||||
if (!obj.setMember(env, key, val)) throw EngineException.ofSyntax("Can't set member '" + key.toReadable(env) + "'");
|
||||
if ((boolean)instr.get(0)) frame.push(val);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -280,7 +251,7 @@ public class InstructionRunner {
|
||||
var val = frame.pop();
|
||||
var obj = frame.pop();
|
||||
|
||||
if (!obj.setMember(env, (String)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'.");
|
||||
if (!obj.setMember(env, (String)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'");
|
||||
if ((boolean)instr.get(1)) frame.push(val);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -289,7 +260,7 @@ public class InstructionRunner {
|
||||
var val = frame.pop();
|
||||
var obj = frame.pop();
|
||||
|
||||
if (!obj.setMember(env, (int)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'.");
|
||||
if (!obj.setMember(env, (int)instr.get(0), val)) throw EngineException.ofSyntax("Can't set member '" + instr.get(0) + "'");
|
||||
if ((boolean)instr.get(1)) frame.push(val);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
@ -298,7 +269,6 @@ public class InstructionRunner {
|
||||
var val = (boolean)instr.get(1) ? frame.peek() : frame.pop();
|
||||
int i = instr.get(0);
|
||||
|
||||
if (!(boolean)instr.get(2) && frame.getVar(i) == null) throw EngineException.ofSyntax("Uninitialized variable");
|
||||
frame.setVar(i, val);
|
||||
frame.codePtr++;
|
||||
|
||||
@ -348,7 +318,7 @@ public class InstructionRunner {
|
||||
var key = frame.pop();
|
||||
var val = frame.pop();
|
||||
|
||||
if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'.");
|
||||
if (!val.deleteMember(env, key)) throw EngineException.ofSyntax("Can't delete member '" + key.toReadable(env) + "'");
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
@ -456,7 +426,7 @@ public class InstructionRunner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Value exexGlobDef(Environment env, Instruction instr, Frame frame) {
|
||||
private static Value execGlobDef(Environment env, Instruction instr, Frame frame) {
|
||||
var name = (String)instr.get(0);
|
||||
|
||||
if (!Value.global(env).hasMember(env, name, false)) {
|
||||
@ -466,7 +436,7 @@ public class InstructionRunner {
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value exexGlobGet(Environment env, Instruction instr, Frame frame) {
|
||||
private static Value execGlobGet(Environment env, Instruction instr, Frame frame) {
|
||||
var name = (String)instr.get(0);
|
||||
if ((boolean)instr.get(1)) {
|
||||
frame.push(Value.global(env).getMember(env, name));
|
||||
@ -481,7 +451,7 @@ public class InstructionRunner {
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value exexGlobSet(Environment env, Instruction instr, Frame frame) {
|
||||
private static Value execGlobSet(Environment env, Instruction instr, Frame frame) {
|
||||
var name = (String)instr.get(0);
|
||||
var keep = (boolean)instr.get(1);
|
||||
var define = (boolean)instr.get(2);
|
||||
@ -497,34 +467,19 @@ public class InstructionRunner {
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execExtend(Environment env, Instruction instr, Frame frame) {
|
||||
var superVal = frame.peek(0);
|
||||
var derivedVal = frame.peek(1);
|
||||
|
||||
if (!(superVal instanceof FunctionValue superFunc)) throw EngineException.ofType("Illegal EXTENDS instruction");
|
||||
if (!(superFunc.prototype instanceof ObjectValue superProto)) throw EngineException.ofType("Illegal EXTENDS instruction");
|
||||
if (!(derivedVal instanceof FunctionValue derivedFunc)) throw EngineException.ofType("Illegal EXTENDS instruction");
|
||||
|
||||
derivedFunc.setPrototype(env, superFunc);
|
||||
derivedFunc.prototype.setPrototype(env, superProto);
|
||||
|
||||
private static Value execLoadArg(Environment env, Instruction instr, Frame frame) {
|
||||
frame.push(frame.args[(int)instr.get(0)]);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execLoadArgsN(Environment env, Instruction instr, Frame frame) {
|
||||
frame.push(frame.argsLen);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Value execLoadArgs(Environment env, Instruction instr, Frame frame) {
|
||||
if ((boolean)instr.get(0) || frame.fakeArgs == null) frame.push(frame.argsVal);
|
||||
else frame.push(frame.fakeArgs);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execLoadRestArgs(Environment env, Instruction instr, Frame frame) {
|
||||
int offset = instr.get(0);
|
||||
var res = new ArrayValue();
|
||||
|
||||
if (offset < frame.args.length) res.copyFrom(frame.args, instr.get(0), 0, frame.args.length - offset);
|
||||
|
||||
frame.push(res);
|
||||
frame.push(frame.argsVal);
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
@ -545,25 +500,6 @@ public class InstructionRunner {
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Value execVarInit(Environment env, Instruction instr, Frame frame) {
|
||||
if ((boolean)instr.get(1) || frame.getVar(instr.get(0)) == null) {
|
||||
frame.setVar(instr.get(0), Value.UNDEFINED);
|
||||
}
|
||||
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execVarFree(Environment env, Instruction instr, Frame frame) {
|
||||
frame.locals[(int)instr.get(0)] = null;
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
private static Value execCapFree(Environment env, Instruction instr, Frame frame) {
|
||||
frame.capturables[(int)instr.get(0) - frame.locals.length] = new Value[1];
|
||||
frame.codePtr++;
|
||||
return null;
|
||||
}
|
||||
|
||||
public static Value exec(Environment env, Instruction instr, Frame frame) {
|
||||
switch (instr.type) {
|
||||
case NOP: return execNop(env, instr, frame);
|
||||
@ -572,7 +508,6 @@ public class InstructionRunner {
|
||||
case THROW_SYNTAX: return execThrowSyntax(env, instr, frame);
|
||||
case CALL: return execCall(env, instr, frame);
|
||||
case CALL_NEW: return execCallNew(env, instr, frame);
|
||||
case CALL_SUPER: return execCallSuper(env, instr, frame);
|
||||
case TRY_START: return execTryStart(env, instr, frame);
|
||||
case TRY_END: return execTryEnd(env, instr, frame);
|
||||
|
||||
@ -593,12 +528,14 @@ public class InstructionRunner {
|
||||
case LOAD_REGEX: return execLoadRegEx(env, instr, frame);
|
||||
case LOAD_GLOB: return execLoadGlob(env, instr, frame);
|
||||
case LOAD_INTRINSICS: return execLoadIntrinsics(env, instr, frame);
|
||||
case LOAD_ARGS: return execLoadArgs(env, instr, frame);
|
||||
case LOAD_REST_ARGS: return execLoadRestArgs(env, instr, frame);
|
||||
case LOAD_CALLEE: return execLoadCallee(env, instr, frame);
|
||||
case LOAD_THIS: return execLoadThis(env, instr, frame);
|
||||
case LOAD_ERROR: return execLoadError(env, instr, frame);
|
||||
|
||||
case LOAD_THIS: return execLoadThis(env, instr, frame);
|
||||
case LOAD_ARG: return execLoadArg(env, instr, frame);
|
||||
case LOAD_ARGS: return execLoadArgs(env, instr, frame);
|
||||
case LOAD_ARGS_N: return execLoadArgsN(env, instr, frame);
|
||||
case LOAD_CALLED: return execLoadCallee(env, instr, frame);
|
||||
|
||||
case DISCARD: return execDiscard(env, instr, frame);
|
||||
case STORE_MEMBER: return execStoreMember(env, instr, frame);
|
||||
case STORE_MEMBER_STR: return execStoreMemberStr(env, instr, frame);
|
||||
@ -617,16 +554,11 @@ public class InstructionRunner {
|
||||
|
||||
case OPERATION: return execOperation(env, instr, frame);
|
||||
|
||||
case GLOB_DEF: return exexGlobDef(env, instr, frame);
|
||||
case GLOB_GET: return exexGlobGet(env, instr, frame);
|
||||
case GLOB_SET: return exexGlobSet(env, instr, frame);
|
||||
case EXTEND: return execExtend(env, instr, frame);
|
||||
case GLOB_DEF: return execGlobDef(env, instr, frame);
|
||||
case GLOB_GET: return execGlobGet(env, instr, frame);
|
||||
case GLOB_SET: return execGlobSet(env, instr, frame);
|
||||
|
||||
case VAR_INIT: return execVarInit(env, instr, frame);
|
||||
case VAR_FREE: return execVarFree(env, instr, frame);
|
||||
case CAP_FREE: return execCapFree(env, instr, frame);
|
||||
|
||||
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + ".");
|
||||
default: throw EngineException.ofSyntax("Invalid instruction " + instr.type.name() + "");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -212,12 +212,12 @@ public class SimpleRepl {
|
||||
|
||||
res.defineOwnMember(env, "setCallable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableCall = args.get(1).toBoolean();
|
||||
func.enableApply = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnMember(env, "setConstructable", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
func.enableNew = args.get(1).toBoolean();
|
||||
func.enableConstruct = args.get(1).toBoolean();
|
||||
return Value.UNDEFINED;
|
||||
}));
|
||||
res.defineOwnMember(env, "invokeType", new NativeFunction(args -> {
|
||||
@ -229,16 +229,14 @@ public class SimpleRepl {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var self = args.get(1);
|
||||
var funcArgs = (ArrayValue)args.get(2);
|
||||
var name = args.get(3).toString(env);
|
||||
|
||||
return func.apply(env, name, self, funcArgs.toArray());
|
||||
return func.apply(env, self, funcArgs.toArray());
|
||||
}));
|
||||
res.defineOwnMember(env, "construct", new NativeFunction(args -> {
|
||||
var func = (FunctionValue)args.get(0);
|
||||
var funcArgs = (ArrayValue)args.get(1);
|
||||
var name = args.get(2).toString(env);
|
||||
|
||||
return func.construct(env, name, funcArgs.toArray());
|
||||
return func.construct(env, funcArgs.toArray());
|
||||
}));
|
||||
|
||||
return res;
|
||||
|
@ -14,12 +14,12 @@ public interface Member {
|
||||
public boolean enumerable;
|
||||
|
||||
@Override public Value get(Environment env, Value self) {
|
||||
if (getter != null) return getter.call(env, false, "", self);
|
||||
if (getter != null) return getter.apply(env, self);
|
||||
else return Value.UNDEFINED;
|
||||
}
|
||||
@Override public boolean set(Environment env, Value val, Value self) {
|
||||
if (setter == null) return false;
|
||||
setter.call(env, false, "", self, val);
|
||||
setter.apply(env, self, val);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user