jscript: Implement caller for function instances and prototype.

Signed-off-by: Gabriel Ivăncescu <gabrielopcode@gmail.com>
This commit is contained in:
Gabriel Ivăncescu 2023-06-22 16:19:01 +03:00 committed by Alexandre Julliard
parent e748ead5cf
commit 426f4bb3ea
4 changed files with 82 additions and 1 deletions

View file

@ -270,6 +270,26 @@ HRESULT Function_invoke(jsdisp_t *func_this, jsval_t vthis, WORD flags, unsigned
return function->vtbl->call(function->dispex.ctx, function, vthis, flags, argc, argv, r);
}
static HRESULT Function_get_caller(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
{
FunctionInstance *function = function_from_jsdisp(jsthis);
call_frame_t *frame;
TRACE("%p\n", jsthis);
for(frame = ctx->call_ctx; frame; frame = frame->prev_frame) {
if(frame->function_instance == &function->dispex) {
if(!frame->prev_frame || !frame->prev_frame->function_instance)
break;
*r = jsval_obj(jsdisp_addref(frame->prev_frame->function_instance));
return S_OK;
}
}
*r = jsval_null();
return S_OK;
}
static HRESULT Function_get_length(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
{
TRACE("%p\n", jsthis);
@ -565,6 +585,7 @@ static const builtin_prop_t Function_props[] = {
{L"arguments", NULL, 0, Function_get_arguments},
{L"bind", Function_bind, PROPF_METHOD|PROPF_ES5|1},
{L"call", Function_call, PROPF_METHOD|1},
{L"caller", NULL, PROPF_HTML, Function_get_caller},
{L"length", NULL, 0, Function_get_length},
{L"toString", Function_toString, PROPF_METHOD}
};
@ -584,6 +605,7 @@ static const builtin_info_t Function_info = {
static const builtin_prop_t FunctionInst_props[] = {
{L"arguments", NULL, 0, Function_get_arguments},
{L"caller", NULL, PROPF_HTML, Function_get_caller},
{L"length", NULL, 0, Function_get_length}
};
@ -769,6 +791,7 @@ static HRESULT InterpretedFunction_set_prototype(script_ctx_t *ctx, jsdisp_t *js
static const builtin_prop_t InterpretedFunction_props[] = {
{L"arguments", NULL, 0, Function_get_arguments},
{L"caller", NULL, PROPF_HTML, Function_get_caller},
{L"length", NULL, 0, Function_get_length},
{L"prototype", NULL, 0, InterpretedFunction_get_prototype, InterpretedFunction_set_prototype}
};
@ -889,6 +912,30 @@ HRESULT create_source_function(script_ctx_t *ctx, bytecode_t *code, function_cod
return S_OK;
}
static HRESULT BindFunction_get_caller(script_ctx_t *ctx, jsdisp_t *jsthis, jsval_t *r)
{
return JS_E_INVALID_ACTION;
}
static const builtin_prop_t BindFunction_props[] = {
{L"arguments", NULL, 0, Function_get_arguments},
{L"caller", NULL, 0, BindFunction_get_caller},
{L"length", NULL, 0, Function_get_length}
};
static const builtin_info_t BindFunction_info = {
JSCLASS_FUNCTION,
Function_value,
ARRAY_SIZE(BindFunction_props),
BindFunction_props,
Function_destructor,
NULL,
NULL,
NULL,
NULL,
Function_gc_traverse
};
static HRESULT BindFunction_call(script_ctx_t *ctx, FunctionInstance *func, jsval_t vthis, unsigned flags,
unsigned argc, jsval_t *argv, jsval_t *r)
{
@ -975,7 +1022,7 @@ static HRESULT create_bind_function(script_ctx_t *ctx, FunctionInstance *target,
BindFunction *function;
HRESULT hres;
hres = create_function(ctx, NULL, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD,
hres = create_function(ctx, &BindFunction_info, &BindFunctionVtbl, FIELD_OFFSET(BindFunction, args[argc]), PROPF_METHOD,
FALSE, NULL, (void**)&function);
if(FAILED(hres))
return hres;

View file

@ -286,6 +286,7 @@ ok(unescape(escape(tmp)) === tmp, "unescape(escape('" + tmp + "')) = " + unescap
ok(Object.prototype.hasOwnProperty('toString'), "Object.prototype.hasOwnProperty('toString') is false");
ok(Object.prototype.hasOwnProperty('isPrototypeOf'), "Object.prototype.hasOwnProperty('isPrototypeOf') is false");
ok(Function.prototype.hasOwnProperty('call'), "Function.prototype.hasOwnProperty('call') is false");
ok(!Function.prototype.hasOwnProperty('caller'), "Function.prototype.hasOwnProperty('caller') is true");
Object();
new Object();

View file

@ -840,6 +840,27 @@ sync_test("for..in", function() {
ok(found === 1, "ondragstart enumerated " + found + " times in document after set to empty string");
});
sync_test("function caller", function() {
ok(Function.prototype.hasOwnProperty("caller"), "caller not prop of Function.prototype");
function test_caller(expected_caller, stop) {
ok(test_caller.caller === expected_caller, "caller = " + test_caller.caller);
if(stop) return;
function nested() {
ok(nested.caller === test_caller, "nested caller = " + nested.caller);
test_caller(nested, true);
ok(test_caller.caller === expected_caller, "caller within nested = " + test_caller.caller);
}
nested();
ok(test_caller.caller === expected_caller, "caller after nested = " + test_caller.caller);
}
ok(test_caller.hasOwnProperty("caller"), "caller not prop of test_caller");
ok(test_caller.caller === null, "test_caller.caller = " + test_caller.caller);
function f1() { test_caller(f1); } f1();
function f2() { test_caller(f2); } f2();
});
sync_test("elem_by_id", function() {
document.body.innerHTML = '<form id="testid" name="testname"></form>';
var v = document.documentMode, found, i;

View file

@ -18,6 +18,7 @@
var E_INVALIDARG = 0x80070057;
var JS_E_PROP_DESC_MISMATCH = 0x800a01bd;
var JS_E_INVALID_ACTION = 0x800a01bd;
var JS_E_NUMBER_EXPECTED = 0x800a1389;
var JS_E_FUNCTION_EXPECTED = 0x800a138a;
var JS_E_DATE_EXPECTED = 0x800a138e;
@ -525,11 +526,14 @@ sync_test("getOwnPropertyDescriptor", function() {
(function() {
test_own_data_prop_desc(arguments, "length", true, false, true);
test_own_data_prop_desc(arguments, "callee", true, false, true);
ok(!("caller" in arguments), "caller in arguments");
})();
test_own_data_prop_desc(String, "prototype", false, false, false);
test_own_data_prop_desc(function(){}, "prototype", true, false, false);
test_own_data_prop_desc(function(){}, "caller", false, false, false);
test_own_data_prop_desc(Function, "prototype", false, false, false);
test_own_data_prop_desc(Function.prototype, "caller", false, false, false);
test_own_data_prop_desc(String.prototype, "constructor", true, false, true);
try {
@ -1136,6 +1140,14 @@ sync_test("bind", function() {
r = f.call(o2);
ok(r === 1, "r = " + r);
try {
f.caller;
ok(false, "expected exception getting f.caller");
}catch(ex) {
var n = ex.number >>> 0;
ok(n === JS_E_INVALID_ACTION, "f.caller threw " + n);
}
f = (function() {
ok(this === o, "this != o");
ok(arguments.length === 1, "arguments.length = " + arguments.length);