feat: add constructor target support

This commit is contained in:
TopchetoEU 2024-12-10 15:37:39 +02:00
parent a329f615cf
commit 3abdd8d3c9
Signed by: topchetoeu
GPG Key ID: 6531B8583E5F6ED4
6 changed files with 58 additions and 23 deletions

View File

@ -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<Frame> KEY = new Key<>();
public static final Key<Stack<Frame>> 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<Frame> get(Environment env) {
if (env.has(KEY)) return env.get(KEY);
else {
var stack = new Stack<Frame>();
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);
}
}

View File

@ -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);
}

View File

@ -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 extends Value> 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;
}

View File

@ -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) {

View File

@ -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) {

View File

@ -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) {