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;
|
import me.topchetoeu.jscript.runtime.values.primitives.numbers.IntValue;
|
||||||
|
|
||||||
public final class Frame {
|
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;
|
public static final EngineException STACK_OVERFLOW;
|
||||||
static {
|
static {
|
||||||
STACK_OVERFLOW = EngineException.ofRange("Stack overflow!");
|
STACK_OVERFLOW = EngineException.ofRange("Stack overflow!");
|
||||||
@ -110,6 +110,7 @@ public final class Frame {
|
|||||||
public final Value[][] capturables;
|
public final Value[][] capturables;
|
||||||
|
|
||||||
public final Value self;
|
public final Value self;
|
||||||
|
public final Value target;
|
||||||
public final Value[] args;
|
public final Value[] args;
|
||||||
public final Value argsVal;
|
public final Value argsVal;
|
||||||
public final Value argsLen;
|
public final Value argsLen;
|
||||||
@ -345,9 +346,11 @@ public final class Frame {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void onPush() {
|
public void onPush() {
|
||||||
|
get(env).push(this);
|
||||||
DebugContext.get(env).onFramePush(env, this);
|
DebugContext.get(env).onFramePush(env, this);
|
||||||
}
|
}
|
||||||
public void onPop() {
|
public void onPop() {
|
||||||
|
get(env).pop();
|
||||||
DebugContext.get(env).onFramePop(env, this);
|
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.env = env;
|
||||||
this.dbg = DebugContext.get(env);
|
this.dbg = DebugContext.get(env);
|
||||||
this.function = func;
|
this.function = func;
|
||||||
this.isNew = isNew;
|
this.isNew = isNew;
|
||||||
|
this.target = target;
|
||||||
|
|
||||||
this.self = self;
|
this.self = self;
|
||||||
this.args = args;
|
this.args = args;
|
||||||
@ -383,4 +387,17 @@ public final class Frame {
|
|||||||
this.capturables = new Value[func.body.capturablesN][1];
|
this.capturables = new Value[func.body.capturablesN][1];
|
||||||
for (var i = 0; i < this.capturables.length; i++) this.capturables[i][0] = Value.UNDEFINED;
|
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) {
|
public Value apply(Environment env, Value self, Value ...args) {
|
||||||
throw EngineException.ofType("Value is not a function");
|
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");
|
throw EngineException.ofType("Value is not a constructor");
|
||||||
}
|
}
|
||||||
|
|
||||||
public final Value constructNoSelf(Environment env, Value ...args) {
|
public final Value constructNoSelf(Environment env, Value ...args) {
|
||||||
var res = new ObjectValue();
|
return this.construct(env, this, args);
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -3,6 +3,7 @@ package me.topchetoeu.jscript.runtime.values.functions;
|
|||||||
|
|
||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
import me.topchetoeu.jscript.common.environment.Environment;
|
||||||
import me.topchetoeu.jscript.runtime.values.Value;
|
import me.topchetoeu.jscript.runtime.values.Value;
|
||||||
|
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||||
import me.topchetoeu.jscript.runtime.values.primitives.UserValue;
|
import me.topchetoeu.jscript.runtime.values.primitives.UserValue;
|
||||||
|
|
||||||
public class Arguments {
|
public class Arguments {
|
||||||
@ -11,6 +12,15 @@ public class Arguments {
|
|||||||
public final Environment env;
|
public final Environment env;
|
||||||
public final boolean isNew;
|
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() {
|
public int n() {
|
||||||
return args.length;
|
return args.length;
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,7 @@ import me.topchetoeu.jscript.common.FunctionBody;
|
|||||||
import me.topchetoeu.jscript.common.environment.Environment;
|
import me.topchetoeu.jscript.common.environment.Environment;
|
||||||
import me.topchetoeu.jscript.runtime.Frame;
|
import me.topchetoeu.jscript.runtime.Frame;
|
||||||
import me.topchetoeu.jscript.runtime.values.Value;
|
import me.topchetoeu.jscript.runtime.values.Value;
|
||||||
|
import me.topchetoeu.jscript.runtime.values.objects.ObjectValue;
|
||||||
|
|
||||||
public final class CodeFunction extends FunctionValue {
|
public final class CodeFunction extends FunctionValue {
|
||||||
public final FunctionBody body;
|
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) {
|
@Override protected Value onApply(Environment ext, Value self, Value... args) {
|
||||||
var frame = new Frame(env, isNew, self, args, this);
|
var frame = new Frame(env, false, null, self, args, this);
|
||||||
|
|
||||||
var res = onCall(frame);
|
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;
|
var proto = target.getMember(env, "prototype");
|
||||||
else return res;
|
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) {
|
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 String toString() { return String.format("function %s(...)", name); }
|
||||||
@Override public Value apply(Environment env, Value self, Value... args) {
|
@Override public Value apply(Environment env, Value self, Value... args) {
|
||||||
if (!enableApply) throw EngineException.ofType("Function cannot be applied");
|
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");
|
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) {
|
@Override public Member getOwnMember(Environment env, KeyCache key) {
|
||||||
|
@ -10,8 +10,11 @@ public final class NativeFunction extends FunctionValue {
|
|||||||
|
|
||||||
public final NativeFunctionRunner action;
|
public final NativeFunctionRunner action;
|
||||||
|
|
||||||
@Override public Value onCall(Environment env, boolean isNew, Value self, Value ...args) {
|
@Override protected Value onApply(Environment env, Value self, Value... args) {
|
||||||
return action.run(new Arguments(env, isNew, self, 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) {
|
public NativeFunction(String name, NativeFunctionRunner action) {
|
||||||
|
Loading…
Reference in New Issue
Block a user