From 3c72034b72014a087eae8d181252c67cb0782e28 Mon Sep 17 00:00:00 2001 From: Jacek Caban Date: Thu, 4 Jun 2020 17:30:22 +0200 Subject: [PATCH] jscript: Add Array.prototype.reduce implementation. Signed-off-by: Jacek Caban Signed-off-by: Alexandre Julliard --- dlls/jscript/array.c | 71 +++++++++++++++++++++++++++++++++++++++- dlls/mshtml/tests/es5.js | 47 ++++++++++++++++++++++++++ 2 files changed, 117 insertions(+), 1 deletion(-) diff --git a/dlls/jscript/array.c b/dlls/jscript/array.c index 5d50b9249e9..7941031e491 100644 --- a/dlls/jscript/array.c +++ b/dlls/jscript/array.c @@ -1048,7 +1048,7 @@ static HRESULT Array_map(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned return hres; } - /* Fixme check IsCallable */ + /* FIXME: check IsCallable */ if(!argc || !is_object_instance(argv[0]) || !get_object(argv[0])) { FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined())); return E_INVALIDARG; @@ -1094,6 +1094,74 @@ static HRESULT Array_map(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned return hres; } +static HRESULT Array_reduce(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) +{ + IDispatch *context_this = NULL, *callback; + jsval_t callback_args[4], acc, new_acc; + BOOL have_value = FALSE; + jsdisp_t *jsthis; + DWORD length, k; + HRESULT hres; + + TRACE("\n"); + + hres = get_length(ctx, vthis, &jsthis, &length); + if(FAILED(hres)) { + FIXME("Could not get length\n"); + return hres; + } + + /* Fixme check IsCallable */ + if(!argc || !is_object_instance(argv[0]) || !get_object(argv[0])) { + FIXME("Invalid arg %s\n", debugstr_jsval(argc ? argv[0] : jsval_undefined())); + return E_INVALIDARG; + } + callback = get_object(argv[0]); + + if(argc > 1) { + have_value = TRUE; + hres = jsval_copy(argv[1], &acc); + if(FAILED(hres)) + return hres; + } + + for(k = 0; k < length; k++) { + hres = jsdisp_get_idx(jsthis, k, &callback_args[1]); + if(hres == DISP_E_UNKNOWNNAME) + continue; + if(FAILED(hres)) + break; + + if(!have_value) { + have_value = TRUE; + acc = callback_args[1]; + continue; + } + + callback_args[0] = acc; + callback_args[2] = jsval_number(k); + callback_args[3] = jsval_obj(jsthis); + hres = disp_call_value(ctx, callback, context_this, DISPATCH_METHOD, ARRAY_SIZE(callback_args), callback_args, &new_acc); + jsval_release(callback_args[1]); + if(FAILED(hres)) + break; + + jsval_release(acc); + acc = new_acc; + } + + if(SUCCEEDED(hres) && !have_value) { + WARN("No array element\n"); + hres = JS_E_INVALID_ACTION; + } + + if(SUCCEEDED(hres) && r) + *r = acc; + else if(have_value) + jsval_release(acc); + return hres; +} + /* ECMA-262 3rd Edition 15.4.4.13 */ static HRESULT Array_unshift(script_ctx_t *ctx, vdisp_t *vthis, WORD flags, unsigned argc, jsval_t *argv, jsval_t *r) @@ -1198,6 +1266,7 @@ static const builtin_prop_t Array_props[] = { {L"map", Array_map, PROPF_METHOD|PROPF_ES5|1}, {L"pop", Array_pop, PROPF_METHOD}, {L"push", Array_push, PROPF_METHOD|1}, + {L"reduce", Array_reduce, PROPF_METHOD|PROPF_ES5|1}, {L"reverse", Array_reverse, PROPF_METHOD}, {L"shift", Array_shift, PROPF_METHOD}, {L"slice", Array_slice, PROPF_METHOD|2}, diff --git a/dlls/mshtml/tests/es5.js b/dlls/mshtml/tests/es5.js index c179ca143f2..f268eba24c6 100644 --- a/dlls/mshtml/tests/es5.js +++ b/dlls/mshtml/tests/es5.js @@ -881,3 +881,50 @@ sync_test("keys", function() { ok(Object.keys.length === 1, "Object.keys.length = " + Object.keys.length); }); + +sync_test("reduce", function() { + var r, array; + + r = [1,2,3].reduce(function(a, value) { return a + value + 10; }); + ok(r === 26, "reduce() returned " + r); + + r = [1,2,3].reduce(function(a, value) { return a + value + 10; }, 1); + ok(r === 37, "reduce() returned " + r); + + r = [1,2,3].reduce(function(a, value) { return a + value; }, "str"); + ok(r === "str123", "reduce() returned " + r); + + array = [1,2,3]; + r = array.reduce(function(a, value, index, src) { + ok(src === array, "src != array"); + return a + "(" + index + "," + value + ")"; + }, "str"); + ok(r === "str(0,1)(1,2)(2,3)", "reduce() returned " + r); + + r = [1,2,3].reduce(function(a, value, index, src) { + src[0] = false; + delete src[1]; + src[2] = "test"; + return a + value; + }, ""); + ok(r === "1test", "reduce() returned " + r); + + r = [1].reduce(function(a) { return 0; }); + ok(r === 1, "[1].reduce() returned " + r); + + r = [1].reduce(function(a) { return 0; }, 2); + ok(r === 0, "[1].reduce(2) returned " + r); + + r = [].reduce(function(a) { return 0; }, 2); + ok(r === 2, "[].reduce(2) returned " + r); + + r = [].reduce(function(a) { return 0; }, undefined); + ok(r === undefined, "[].reduce(undefined) returned " + r); + + try { + [].reduce(function(a) { return 0; }); + ok(false, "expected exception"); + }catch(e) {trace(e.message);} + + ok(Array.prototype.reduce.length === 1, "Array.prototype.reduce.length = " + Array.prototype.reduce.length); +});