jscript: Implement Function.prototype.bind's thisArg properly.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
Gabriel Ivăncescu 2022-10-14 22:14:43 +03:00 committed by Alexandre Julliard
parent 0e49547c7a
commit 35a6555929
2 changed files with 38 additions and 19 deletions

View file

@ -57,7 +57,7 @@ typedef struct {
typedef struct {
FunctionInstance function;
FunctionInstance *target;
IDispatch *this;
jsval_t this;
unsigned argc;
jsval_t args[1];
} BindFunction;
@ -70,7 +70,7 @@ typedef struct {
unsigned argc;
} ArgumentsInstance;
static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,IDispatch*,unsigned,jsval_t*,jsdisp_t**r);
static HRESULT create_bind_function(script_ctx_t*,FunctionInstance*,jsval_t,unsigned,jsval_t*,jsdisp_t**r);
static inline FunctionInstance *function_from_jsdisp(jsdisp_t *jsdisp)
{
@ -448,7 +448,7 @@ static HRESULT Function_call(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsig
static HRESULT Function_bind(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsigned argc, jsval_t *argv,
jsval_t *r)
{
IDispatch *bound_this = NULL;
jsval_t bound_this = jsval_undefined();
FunctionInstance *function;
jsdisp_t *new_function;
HRESULT hres;
@ -459,18 +459,19 @@ static HRESULT Function_bind(script_ctx_t *ctx, jsval_t vthis, WORD flags, unsig
return JS_E_FUNCTION_EXPECTED;
if(argc < 1) {
FIXME("no this argument\n");
return E_NOTIMPL;
}
if(is_object_instance(argv[0])) {
bound_this = get_object(argv[0]);
}else if(!is_null(argv[0])) {
FIXME("%s is not an object instance\n", debugstr_jsval(argv[0]));
return E_NOTIMPL;
argc = 1;
}else if(is_null(argv[0])) {
bound_this = argv[0];
}else if(!is_undefined(argv[0])) {
IDispatch *obj;
hres = to_object(ctx, argv[0], &obj);
if(FAILED(hres))
return hres;
bound_this = jsval_disp(obj);
}
hres = create_bind_function(ctx, function, bound_this, argc - 1, argv + 1, &new_function);
jsval_release(bound_this);
if(FAILED(hres))
return hres;
@ -849,8 +850,7 @@ static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsva
memcpy(call_args + function->argc, argv, argc * sizeof(*call_args));
}
hres = function->target->vtbl->call(ctx, function->target, function->this ? jsval_disp(function->this) : jsval_null(),
flags, call_argc, call_args, r);
hres = function->target->vtbl->call(ctx, function->target, function->this, flags, call_argc, call_args, r);
heap_free(call_args);
return hres;
@ -877,8 +877,7 @@ static void BindFunction_destructor(FunctionInstance *func)
for(i = 0; i < function->argc; i++)
jsval_release(function->args[i]);
jsdisp_release(&function->target->dispex);
if(function->this)
IDispatch_Release(function->this);
jsval_release(function->this);
}
static const function_vtbl_t BindFunctionVtbl = {
@ -888,7 +887,7 @@ static const function_vtbl_t BindFunctionVtbl = {
BindFunction_destructor
};
static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, IDispatch *bound_this, unsigned argc,
static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target, jsval_t bound_this, unsigned argc,
jsval_t *argv, jsdisp_t **ret)
{
BindFunction *function;
@ -902,8 +901,11 @@ static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target,
jsdisp_addref(&target->dispex);
function->target = target;
if(bound_this)
IDispatch_AddRef(function->this = bound_this);
hres = jsval_copy(bound_this, &function->this);
if(FAILED(hres)) {
jsdisp_release(&function->function.dispex);
return hres;
}
for(function->argc = 0; function->argc < argc; function->argc++) {
hres = jsval_copy(argv[function->argc], function->args + function->argc);

View file

@ -1188,6 +1188,23 @@ sync_test("bind", function() {
ok(t != a, "t == a");
ok(Function.prototype.bind.length === 1, "Function.prototype.bind.length = " + Function.prototype.bind.length);
((function() { ok(this === window, "bind() this = " + this); }).bind())();
((function() { ok(this === window, "bind(undefined) = " + this); }).bind(undefined))();
((function() { ok(this === window, "bind(nullDisp) = " + this); }).bind(external.nullDisp))();
((function() {
ok(typeof(this) === "object", "bind(42) typeof(this) = " + typeof(this));
ok(this.valueOf() === 42, "bind(42) this = " + this);
}).bind(42))();
r = (Object.prototype.toString.bind())();
ok(r === "[object Undefined]", "toString.bind() returned " + r);
r = (Object.prototype.toString.bind(undefined))();
ok(r === "[object Undefined]", "toString.bind(undefined) returned " + r);
r = (Object.prototype.toString.bind(null))();
ok(r === "[object Null]", "toString.bind(null) returned " + r);
r = (Object.prototype.toString.bind(external.nullDisp))();
ok(r === "[object Null]", "toString.bind(nullDisp) returned " + r);
});
sync_test("keys", function() {