diff --git a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java index cf22b35..f5f8b2f 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/Frame.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/Frame.java @@ -16,7 +16,7 @@ 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 = new Key<>(); + public static final Key> KEY = new Key<>(); public static final EngineException STACK_OVERFLOW; static { STACK_OVERFLOW = EngineException.ofRange("Stack overflow!"); @@ -110,6 +110,7 @@ public final class Frame { public final Value[][] capturables; public final Value self; + public final Value target; public final Value[] args; public final Value argsVal; public final Value argsLen; @@ -345,9 +346,11 @@ public final class Frame { } public void onPush() { + get(env).push(this); DebugContext.get(env).onFramePush(env, this); } public void onPop() { + get(env).pop(); DebugContext.get(env).onFramePop(env, this); } @@ -366,11 +369,12 @@ public final class Frame { }; } - public Frame(Environment env, boolean isNew, Value self, Value[] args, CodeFunction func) { + public Frame(Environment env, boolean isNew, Value target, Value self, Value[] args, CodeFunction func) { this.env = env; this.dbg = DebugContext.get(env); this.function = func; this.isNew = isNew; + this.target = target; this.self = self; this.args = args; @@ -383,4 +387,17 @@ public final class Frame { this.capturables = new Value[func.body.capturablesN][1]; for (var i = 0; i < this.capturables.length; i++) this.capturables[i][0] = Value.UNDEFINED; } + + public static Stack get(Environment env) { + if (env.has(KEY)) return env.get(KEY); + else { + var stack = new Stack(); + env.add(KEY, stack); + return stack; + } + } + public static Frame get(Environment env, int i) { + var stack = get(env); + return stack.get(stack.size() - i - 1); + } } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java index 0d41128..72e4ba2 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/Value.java @@ -85,20 +85,12 @@ public abstract class Value { public Value apply(Environment env, Value self, Value ...args) { throw EngineException.ofType("Value is not a function"); } - public Value construct(Environment env, Value self, Value ...args) { + public Value construct(Environment env, Value target, Value ...args) { throw EngineException.ofType("Value is not a constructor"); } public final Value constructNoSelf(Environment env, Value ...args) { - var res = new ObjectValue(); - var proto = getMember(env, StringValue.of("prototype")); - - if (proto instanceof ObjectValue) res.setPrototype(env, (ObjectValue)proto); - - var ret = this.construct(env, res, args); - - if (ret == Value.UNDEFINED || ret.isPrimitive()) return res; - return ret; + return this.construct(env, this, args); } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java index 984cb2b..f7a3dac 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/Arguments.java @@ -3,6 +3,7 @@ package me.topchetoeu.jscript.runtime.values.functions; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; import me.topchetoeu.jscript.runtime.values.primitives.UserValue; public class Arguments { @@ -11,6 +12,15 @@ public class Arguments { public final Environment env; public final boolean isNew; + public final T setTargetProto(T obj) { + if (!self.isPrimitive()) { + var proto = self.getMember(env, "prototype"); + if (proto instanceof ObjectValue objProto) self.setPrototype(env, objProto); + else if (proto == Value.NULL) self.setPrototype(env, null); + } + return obj; + } + public int n() { return args.length; } diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java index 0b9568a..e43511d 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/CodeFunction.java @@ -4,6 +4,7 @@ import me.topchetoeu.jscript.common.FunctionBody; import me.topchetoeu.jscript.common.environment.Environment; import me.topchetoeu.jscript.runtime.Frame; import me.topchetoeu.jscript.runtime.values.Value; +import me.topchetoeu.jscript.runtime.values.objects.ObjectValue; public final class CodeFunction extends FunctionValue { public final FunctionBody body; @@ -24,13 +25,24 @@ public final class CodeFunction extends FunctionValue { } } - @Override public Value onCall(Environment env, boolean isNew, Value self, Value ...args) { - var frame = new Frame(env, isNew, self, args, this); - + @Override protected Value onApply(Environment ext, Value self, Value... args) { + var frame = new Frame(env, false, null, self, args, this); var res = onCall(frame); + return res; + } + @Override protected Value onConstruct(Environment ext, Value target, Value... args) { + var self = new ObjectValue(); - if (isNew) return frame.self; - else return res; + var proto = target.getMember(env, "prototype"); + if (proto instanceof ObjectValue) self.setPrototype(env, (ObjectValue)proto); + else if (proto == Value.NULL) self.setPrototype(env, null); + + var frame = new Frame(env, true, target, self, args, this); + + var ret = onCall(frame); + + if (ret == Value.UNDEFINED || ret.isPrimitive()) return self; + return ret; } public CodeFunction(Environment env, String name, FunctionBody body, Value[][] captures) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java index cef538e..d080ef2 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/FunctionValue.java @@ -52,16 +52,17 @@ public abstract class FunctionValue extends ObjectValue { } }; - protected abstract Value onCall(Environment ext, boolean isNew, Value thisArg, Value ...args); + protected abstract Value onApply(Environment ext, Value thisArg, Value ...args); + protected abstract Value onConstruct(Environment ext, Value target, Value ...args); @Override public String toString() { return String.format("function %s(...)", name); } @Override public Value apply(Environment env, Value self, Value... args) { if (!enableApply) throw EngineException.ofType("Function cannot be applied"); - return onCall(env, false, self, args); + return onApply(env, self, args); } - @Override public Value construct(Environment env, Value self, Value... args) { + @Override public Value construct(Environment env, Value target, Value... args) { if (!enableConstruct) throw EngineException.ofType("Function cannot be constructed"); - return onCall(env, true, self, args); + return onConstruct(env, target, args); } @Override public Member getOwnMember(Environment env, KeyCache key) { diff --git a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java index a4a81e3..bbdc58f 100644 --- a/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java +++ b/src/main/java/me/topchetoeu/jscript/runtime/values/functions/NativeFunction.java @@ -10,8 +10,11 @@ public final class NativeFunction extends FunctionValue { public final NativeFunctionRunner action; - @Override public Value onCall(Environment env, boolean isNew, Value self, Value ...args) { - return action.run(new Arguments(env, isNew, self, args)); + @Override protected Value onApply(Environment env, Value self, Value... args) { + return action.run(new Arguments(env, false, self, args)); + } + @Override protected Value onConstruct(Environment env, Value target, Value... args) { + return action.run(new Arguments(env, true, target, args)); } public NativeFunction(String name, NativeFunctionRunner action) {