feat: add constructor target support
This commit is contained in:
parent
a329f615cf
commit
3abdd8d3c9
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user