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