From d431319f6d1b1677e72d7b2d04413e98d7dc7018 Mon Sep 17 00:00:00 2001 From: theMackabu Date: Mon, 4 May 2026 01:57:32 -0700 Subject: [PATCH] handle explicit function length --- examples/results.txt | 4 ++-- examples/spec/functions.js | 10 ++++++++++ include/silver/engine.h | 3 ++- src/silver/ast.c | 22 ++++++++++++++++++++++ src/silver/compiler.c | 28 ++++++++++++++++++++++++++-- src/silver/glue.c | 2 +- src/silver/ops/upvalues.h | 2 +- 7 files changed, 64 insertions(+), 7 deletions(-) diff --git a/examples/results.txt b/examples/results.txt index 99b0f7c..16abbb4 100644 --- a/examples/results.txt +++ b/examples/results.txt @@ -1047,9 +1047,9 @@ compat-table/es6/regex.flags.y.js: OK compat-table/es6/regex.flags.y.lastIndex.js: OK compat-table/es6/rest-params.arguments.js: OK compat-table/es6/rest-params.basic.js: OK -compat-table/es6/rest-params.length.js: failed +compat-table/es6/rest-params.length.js: OK compat-table/es6/rest-params.new-function.js: OK -compat-table/es6/rest-params.no-setter.js: failed +compat-table/es6/rest-params.no-setter.js: OK compat-table/es6/spread.array-call.js: OK compat-table/es6/spread.array-literal.js: OK compat-table/es6/spread.astral-call.js: OK diff --git a/examples/spec/functions.js b/examples/spec/functions.js index 8aa4c81..b15f214 100644 --- a/examples/spec/functions.js +++ b/examples/spec/functions.js @@ -34,6 +34,16 @@ function restParams(first, ...rest) { return first + rest.length; } test('rest params', restParams(1, 2, 3, 4), 4); +test('rest params length excludes rest', restParams.length, 1); +test('rest-only params length is zero', ((...rest) => rest.length).length, 0); + +let restSetterRejected = false; +try { + eval('({ set value(...rest) {} })'); +} catch (e) { + restSetterRejected = true; +} +test('rest params rejected in setter', restSetterRejected, true); function spreadCall(a, b, c) { return a + b + c; diff --git a/include/silver/engine.h b/include/silver/engine.h index 2454980..731442b 100644 --- a/include/silver/engine.h +++ b/include/silver/engine.h @@ -180,7 +180,6 @@ struct sv_func { int max_locals; int max_stack; int local_type_count; - int param_count; int upvalue_count; int srcpos_count; int source_line; @@ -190,6 +189,8 @@ struct sv_func { uint16_t ic_count; uint16_t obj_site_count; + uint16_t param_count; + uint16_t function_length; bool is_strict; bool is_arrow; diff --git a/src/silver/ast.c b/src/silver/ast.c index fbbcad5..39f8953 100644 --- a/src/silver/ast.c +++ b/src/silver/ast.c @@ -859,6 +859,26 @@ static sv_ast_t *parse_array(P) { return n; } +static bool validate_accessor_params(P, sv_ast_t *fn, uint16_t flags) { + if (!(flags & (FN_GETTER | FN_SETTER)) || !fn) return true; + + if ((flags & FN_GETTER) && fn->args.count != 0) { + SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Getter must not have parameters"); + return false; + } + + if ( + (flags & FN_SETTER) && ( + fn->args.count != 1 || + (fn->args.count == 1 && fn->args.items[0] && fn->args.items[0]->type == N_REST)) + ) { + SV_MKERR_TYPED(JS, JS_ERR_SYNTAX, "Setter must have exactly one non-rest parameter"); + return false; + } + + return true; +} + static sv_ast_t *parse_object(P) { CONSUME(); sv_ast_t *n = mk(N_OBJECT); @@ -939,6 +959,7 @@ static sv_ast_t *parse_object(P) { } prop->right = parse_func(p); + if (!validate_accessor_params(p, prop->right, prop->flags)) return n; prop->right->flags |= FN_METHOD; prop->right->src_off = prop->src_off; sv_ast_list_push(&n->args, prop); @@ -1496,6 +1517,7 @@ static sv_ast_t *parse_class(P) { if (NEXT() == TOK_LPAREN) { method->right = parse_func(p); + if (!validate_accessor_params(p, method->right, method->flags)) return cls; method->right->flags |= (flags & (FN_ASYNC | FN_GENERATOR)) | FN_METHOD; method->right->src_off = method_src_off; } else if (TOK == TOK_ASSIGN) { diff --git a/src/silver/compiler.c b/src/silver/compiler.c index d337bcc..fc7e51f 100644 --- a/src/silver/compiler.c +++ b/src/silver/compiler.c @@ -4757,16 +4757,20 @@ void compile_class(sv_compiler_t *c, sv_ast_t *node) { memcpy(fn->local_types, comp.slot_types, (size_t)ncopy * sizeof(sv_type_info_t)); } } - fn->param_count = comp.param_count; + + fn->param_count = (uint16_t)comp.param_count; + fn->function_length = (uint16_t)comp.param_count; fn->is_strict = comp.is_strict; fn->filename = c->js->filename; fn->source_line = (int)node->line; + if (node->str && node->len > 0) { char *name = code_arena_bump(node->len + 1); memcpy(name, node->str, node->len); name[node->len] = '\0'; fn->name = name; } + sv_compile_ctx_cleanup(&comp); int idx = add_constant(c, mkval(T_NTARG, (uintptr_t)fn)); emit_op(c, OP_CLOSURE); @@ -4894,11 +4898,29 @@ static bool func_params_contain_await(const sv_ast_t *node) { return false; } +static uint16_t function_length_from_params(const sv_ast_t *node) { + if (!node) return 0; + uint16_t length = 0; + for (int i = 0; i < node->args.count; i++) { + sv_ast_t *param = node->args.items[i]; + if (!param || param->type == N_REST || param->type == N_ASSIGN_PAT) break; + length++; + } + return length; +} + sv_func_t *compile_function_body( sv_compiler_t *enclosing, sv_ast_t *node, sv_compile_mode_t mode ) { + if (node->args.count > UINT16_MAX) { + js_mkerr_typed( + enclosing->js, JS_ERR_SYNTAX, + "too many function parameters"); + return NULL; + } + if ((node->flags & FN_ASYNC) && func_params_contain_await(node)) { js_mkerr_typed( enclosing->js, JS_ERR_SYNTAX, @@ -5321,7 +5343,9 @@ sv_func_t *compile_function_body( memcpy(func->local_types, comp.slot_types, (size_t)ncopy * sizeof(sv_type_info_t)); } } - func->param_count = comp.param_count; + + func->param_count = (uint16_t)comp.param_count; + func->function_length = function_length_from_params(node); func->is_strict = comp.is_strict; func->is_arrow = comp.is_arrow; diff --git a/src/silver/glue.c b/src/silver/glue.c index 53932f7..b4cf424 100644 --- a/src/silver/glue.c +++ b/src/silver/glue.c @@ -638,7 +638,7 @@ ant_value_t jit_helper_closure( ant_value_t module_ctx = sv_get_current_closure_module_ctx(js, mkval(T_FUNC, (uintptr_t)parent_closure)); js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator && !child->is_async); - js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); + js_setprop(js, func_obj, js->length_str, tov((double)child->function_length)); js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); if (is_object_type(module_ctx)) diff --git a/src/silver/ops/upvalues.h b/src/silver/ops/upvalues.h index a1173c6..deb7bf5 100644 --- a/src/silver/ops/upvalues.h +++ b/src/silver/ops/upvalues.h @@ -136,7 +136,7 @@ static inline ant_value_t sv_op_closure( ant_value_t module_ctx = sv_get_current_closure_module_ctx(js, frame->callee); js_mark_constructor(func_obj, !child->is_arrow && !child->is_method && !child->is_generator && !child->is_async); - js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); + js_setprop(js, func_obj, js->length_str, tov((double)child->function_length)); js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); if (is_object_type(module_ctx)) js_set_slot_wb(js, func_obj, SLOT_MODULE_CTX, module_ctx); -- 2.51.2