From 0d14afabec573657c5c0e086103584817cafdbb5 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 13:17:01 +0200 Subject: [PATCH 01/11] Improve arguments parsing (see #2283) --- www/src/py_utils.js | 258 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 257 insertions(+), 1 deletion(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index e9bf75bd5..c1b03a79f 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -85,7 +85,9 @@ function unexpected_keyword(fname, k){ var empty = {} -$B.args0 = function(f, args){ + +// Original args0 used to construct error message when raising an exception. +function args0(f, args){ // Called by user-defined functions / methods var arg_names = f.$infos.arg_names, code = f.$infos.__code__, @@ -101,6 +103,260 @@ $B.args0 = function(f, args){ code.co_posonlyargcount, code.co_kwonlyargcount) } +// My new implementation of argument parsing. +// Should be faster and can still be improved by precomputing values and changing how it is called (see comments). +// Some conditions can be removed if functions had different functions to parse their arguments depending on their parameters (see comments). +// Currently, I let the original args0 function handle the construction of the exception when I detect an error. +// Notation : +// - params = in the function declaration. +// - args = in the function call. +function args0_NEW(fct, _args) { + + // Last argument should either be "null" or named arguments "[{}, ...]". + // This enables to remove the strange "{$kw:[{}, ...]}" structure. + // Below a way to convert the currently passed arguments to the format I use. + // If you don't want to change it, I can modify the code to remove the costly convertion. + /**/ + const args = [..._args]; + if(args[args.length-1]?.$kw === undefined) + args.push(null); + else + args[args.length-1] = args[args.length-1].$kw; + /**/ + //const args = _args; + + const result = {}; + + // using const should enable the browser to perform some optimisation. + const $INFOS = fct.$infos; + const $CODE = $INFOS.__code__; + + const PARAMS_NAMES = $INFOS.arg_names; + const PARAMS_POS_COUNT = $CODE.co_argcount; + const PARAMS_NAMED_COUNT = $CODE.co_kwonlyargcount; + + const PARAMS_VARARGS_NAME = $INFOS.vararg; + const PARAMS_KWARGS_NAME = $INFOS.kwarg; + + const PARAMS_POS_DEFAULTS = $INFOS.__defaults__; + const PARAMS_POS_DEFAULTS_COUNT = PARAMS_POS_DEFAULTS.length; + + const ARGS_POS_COUNT = args.length-1; + const ARGS_NAMED = args[ARGS_POS_COUNT]; + const PARAMS_POS_DEFAULTS_OFFSET= PARAMS_POS_COUNT - PARAMS_POS_DEFAULTS_COUNT; + + // process positional arguments => positional parameters... + const min = Math.min( ARGS_POS_COUNT, PARAMS_POS_COUNT ); + let offset = 0; + for( ; offset < min ; ++offset) + result[ PARAMS_NAMES[offset] ] = args[offset]; + + // process positional arguments => vargargs parameters... + if( PARAMS_VARARGS_NAME !== null ) + result[PARAMS_VARARGS_NAME] = $B.fast_tuple( args.slice( PARAMS_POS_COUNT, -1 ) ); + // maybe there is a faster way to build a tuple from a subset of an array. + else if( ARGS_POS_COUNT > PARAMS_POS_COUNT ) { + args0(fct, _args); + throw new Error('Too much positional arguments given (args0 should have raised an error) !'); + } + + + // if no named arguments has been given... + if( ARGS_NAMED === null ) { + + // Handle default positional parameters... + + if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { + args0(fct, _args); + throw new Error('Not enough positional arguments given (args0 should have raised an error) !'); + } + + for(let i = offset - PARAMS_POS_DEFAULTS_OFFSET; + i < PARAMS_POS_DEFAULTS_COUNT; + ++i) + result[ PARAMS_NAMES[offset++] ] = PARAMS_POS_DEFAULTS[i]; + + // Handle kwargs parameters... + if( PARAMS_KWARGS_NAME !== null ) + result[PARAMS_KWARGS_NAME] = __BRYTHON__.obj_dict({}); + + // Shortcut : no named parameters. + if( PARAMS_NAMED_COUNT === 0 ) + return result; + + // Handle defaults value for named parameters. + // Optimize: precompute the number of named parameters with a default value, or just a boolean ? + + const kwargs_defaults= $INFOS.__kwdefaults__?.$jsobj ?? $INFOS.__kwdefaults__?.$strings ?? {}; // costs a little... + const named_default_keys = Object.keys(kwargs_defaults); // this operation is costly... + if( named_default_keys.length < PARAMS_NAMED_COUNT ) { + args0(fct, _args); + throw new Error('Named argument expected (args0 should have raised an error) !'); + } + + // The loop is a little costly... + // Test if using an array of name and values is faster than using an object... + for(let i = 0; i < named_default_keys.length; ++i) { + const key = named_default_keys[i]; + result[key] = kwargs_defaults[key]; + } + + return result; + } + + const kwargs_defaults= $INFOS.__kwdefaults__?.$jsobj ?? $INFOS.__kwdefaults__?.$strings ?? {}; // costs a little... + + // Construct the list of default values... + // Optimize : I'd need an object containing ALL default values instead of having to build one... + // If not done, I can work on it to remove the construction of this object (which cost a little). + // Note: I should exclude posonly default parameters... (we don't need them as remaining positional only parameters'd be consumed next) + const kargs_defaults= { ... kwargs_defaults } //costly + for(let i = 0; i < PARAMS_POS_DEFAULTS_COUNT; ++i) // costly + kargs_defaults[PARAMS_NAMES[PARAMS_POS_DEFAULTS_OFFSET+i]] = PARAMS_POS_DEFAULTS[i]; + + // Consume remaining positional only parameters (no positional arguments given, so expect default value). + const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; + if( offset < PARAMS_POSONLY_COUNT ) { + + if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { + args0(fct, _args); + throw new Error('Not enough positional parameters given (args0 should have raised an error) !'); + } + + const max = PARAMS_POS_DEFAULTS_COUNT - (PARAMS_POS_COUNT - PARAMS_POSONLY_COUNT); + + // default parameters + for(let i = offset - PARAMS_POS_DEFAULTS_OFFSET; + i < max; + ++i) + result[ PARAMS_NAMES[offset++] ] = PARAMS_POS_DEFAULTS[i]; + } + + // No **kwargs parameter (i.e. unknown name = error). + if( PARAMS_KWARGS_NAME === null ) { + + let nb_named_args = 0; + + for(let id = 0; id < ARGS_NAMED.length; ++id ) { + + const _kargs = ARGS_NAMED[id] + const kargs = _kargs.$jsobj ?? _kargs.$strings ?? _kargs; // I don't think I can do better. + + const keys = Object.keys(kargs); + nb_named_args += keys.length; + + for(let k = 0; k < keys.length; ++k) { + + const argname = keys[k]; + result[ argname ] = kargs[argname]; + } + } + + // Checks default values... + // What is quicker ? An object or 1 array of name (with indexOf) and 1 array of values ? + let found = 0; + for(let ioffset = offset; ioffset < PARAMS_NAMES.length; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) + continue; + + if( ! (key in kargs_defaults) ) { + args0(fct, _args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = kargs_defaults[key]; + ++found; + } + + // PARAMS_NAMES.length - offset = the number of expected named arguments. + // found + nb_named_args = the number of given named arguments + the number of named arguments we found a default value for. + // If they aren't equal, we either gave several times the same argument or gave an inexisting name. + if( found + nb_named_args !== PARAMS_NAMES.length - offset) { + args0(fct, _args); + throw new Error('Inexistant or duplicate named arguments (args0 should have raised an error) !'); + } + + return result; + } + + + // With **kwargs parameter (i.e. unknown name = put in extra). + + const extra = {}; + + // we count the number of arguments given to normal named parameters and the number given to **kwargs. + let nb_named_args = 0; + let nb_extra_args = 0; + + for(let id = 0; id < ARGS_NAMED.length; ++id ) { + + const _kargs = ARGS_NAMED[id] + const kargs = _kargs.$jsobj ?? _kargs.$strings ?? _kargs; + const keys = Object.keys(kargs); + + for(let k = 0; k < keys.length; ++k) { + + const argname = keys[k]; + + // We search the name starting from non-positional only parameters. + if( PARAMS_NAMES.indexOf(argname, PARAMS_POSONLY_COUNT) !== -1 ) { + result[ argname ] = kargs[argname]; + ++nb_named_args; + } else { + extra[ argname ] = kargs[argname]; + ++nb_extra_args; + } + } + } + + // Same as "No **kwargs parameter". + // Checks default values... + // What is quicker ? An object or 1 array of name (with indexOf) and 1 array of values ? + let found = 0; + for(let ioffset = offset; ioffset < PARAMS_NAMES.length; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) + continue; + + if( ! (key in kargs_defaults) ) { + args0(fct, _args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = kargs_defaults[key]; + ++found; + } + + // Same as "No **kwargs parameter". + // PARAMS_NAMES.length - offset = the number of expected named arguments. + // found + nb_named_args = the number of given named arguments + the number of named arguments we found a default value for. + // If they aren't equal, we either gave several times the same argument or gave an inexisting name. + + if( found + nb_named_args !== PARAMS_NAMES.length - offset) { + args0(fct, _args); + throw new Error('Inexistant or duplicate named arguments (args0 should have raised an error) !'); + } + + // verify if the number of extra arguments (**kargs) found matches the numbers of elements in extra. + // if not, it means that the same name has been given several times. + if( Object.keys(extra).length !== nb_extra_args ) { + args0(fct, _args); + throw new Error('Duplicate name given to **kargs parameter (args0 should have raised an error) !'); + } + + result[PARAMS_KWARGS_NAME] = __BRYTHON__.obj_dict(extra); + + return result; +} + + +//$B.args0 = args0; +$B.args0 = args0_NEW; + + $B.args = function(fname, argcount, slots, var_names, args, $dobj, vararg, kwarg, nb_posonly){ // Called by built-in functions / methods From 76b9ec4666f18f84c2166227b8a4731693952b26 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 19:55:18 +0200 Subject: [PATCH 02/11] Avoid arguments copy --- www/src/py_utils.js | 36 +++++++++++++++++++++++------------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index c1b03a79f..b00cf899d 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -110,13 +110,23 @@ function args0(f, args){ // Notation : // - params = in the function declaration. // - args = in the function call. -function args0_NEW(fct, _args) { +function args0_NEW(fct, args) { // Last argument should either be "null" or named arguments "[{}, ...]". // This enables to remove the strange "{$kw:[{}, ...]}" structure. // Below a way to convert the currently passed arguments to the format I use. // If you don't want to change it, I can modify the code to remove the costly convertion. /**/ + //const args = _args; // should remove this line... + const HAS_KW = args[args.length-1]?.$kw !== undefined; + let ARGS_POS_COUNT = args.length; + let ARGS_NAMED = null; + + if( HAS_KW ) { + --ARGS_POS_COUNT; + ARGS_NAMED = args[ARGS_POS_COUNT].$kw + } + /* const args = [..._args]; if(args[args.length-1]?.$kw === undefined) args.push(null); @@ -141,8 +151,6 @@ function args0_NEW(fct, _args) { const PARAMS_POS_DEFAULTS = $INFOS.__defaults__; const PARAMS_POS_DEFAULTS_COUNT = PARAMS_POS_DEFAULTS.length; - const ARGS_POS_COUNT = args.length-1; - const ARGS_NAMED = args[ARGS_POS_COUNT]; const PARAMS_POS_DEFAULTS_OFFSET= PARAMS_POS_COUNT - PARAMS_POS_DEFAULTS_COUNT; // process positional arguments => positional parameters... @@ -153,10 +161,12 @@ function args0_NEW(fct, _args) { // process positional arguments => vargargs parameters... if( PARAMS_VARARGS_NAME !== null ) - result[PARAMS_VARARGS_NAME] = $B.fast_tuple( args.slice( PARAMS_POS_COUNT, -1 ) ); + // can be speed up if arguments is an array in the first place + result[PARAMS_VARARGS_NAME] = $B.fast_tuple( Array.prototype.slice.call(args, PARAMS_POS_COUNT, HAS_KW ? -1 : undefined ) ); + //result[PARAMS_VARARGS_NAME] = $B.fast_tuple( args.slice( PARAMS_POS_COUNT, HAS_KW ? -1 : undefined ) ); // maybe there is a faster way to build a tuple from a subset of an array. else if( ARGS_POS_COUNT > PARAMS_POS_COUNT ) { - args0(fct, _args); + args0(fct, args); throw new Error('Too much positional arguments given (args0 should have raised an error) !'); } @@ -167,7 +177,7 @@ function args0_NEW(fct, _args) { // Handle default positional parameters... if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { - args0(fct, _args); + args0(fct, args); throw new Error('Not enough positional arguments given (args0 should have raised an error) !'); } @@ -190,7 +200,7 @@ function args0_NEW(fct, _args) { const kwargs_defaults= $INFOS.__kwdefaults__?.$jsobj ?? $INFOS.__kwdefaults__?.$strings ?? {}; // costs a little... const named_default_keys = Object.keys(kwargs_defaults); // this operation is costly... if( named_default_keys.length < PARAMS_NAMED_COUNT ) { - args0(fct, _args); + args0(fct, args); throw new Error('Named argument expected (args0 should have raised an error) !'); } @@ -219,7 +229,7 @@ function args0_NEW(fct, _args) { if( offset < PARAMS_POSONLY_COUNT ) { if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { - args0(fct, _args); + args0(fct, args); throw new Error('Not enough positional parameters given (args0 should have raised an error) !'); } @@ -262,7 +272,7 @@ function args0_NEW(fct, _args) { continue; if( ! (key in kargs_defaults) ) { - args0(fct, _args); + args0(fct, args); throw new Error('Missing a named arguments (args0 should have raised an error) !'); } @@ -274,7 +284,7 @@ function args0_NEW(fct, _args) { // found + nb_named_args = the number of given named arguments + the number of named arguments we found a default value for. // If they aren't equal, we either gave several times the same argument or gave an inexisting name. if( found + nb_named_args !== PARAMS_NAMES.length - offset) { - args0(fct, _args); + args0(fct, args); throw new Error('Inexistant or duplicate named arguments (args0 should have raised an error) !'); } @@ -322,7 +332,7 @@ function args0_NEW(fct, _args) { continue; if( ! (key in kargs_defaults) ) { - args0(fct, _args); + args0(fct, args); throw new Error('Missing a named arguments (args0 should have raised an error) !'); } @@ -336,14 +346,14 @@ function args0_NEW(fct, _args) { // If they aren't equal, we either gave several times the same argument or gave an inexisting name. if( found + nb_named_args !== PARAMS_NAMES.length - offset) { - args0(fct, _args); + args0(fct, args); throw new Error('Inexistant or duplicate named arguments (args0 should have raised an error) !'); } // verify if the number of extra arguments (**kargs) found matches the numbers of elements in extra. // if not, it means that the same name has been given several times. if( Object.keys(extra).length !== nb_extra_args ) { - args0(fct, _args); + args0(fct, args); throw new Error('Duplicate name given to **kargs parameter (args0 should have raised an error) !'); } From 2e177ba7b944733626b5b60f20528dc2c7e16c78 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 19:57:53 +0200 Subject: [PATCH 03/11] Improve very slightly *args --- www/src/py_utils.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index b00cf899d..bb5ee1e3a 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -162,7 +162,7 @@ function args0_NEW(fct, args) { // process positional arguments => vargargs parameters... if( PARAMS_VARARGS_NAME !== null ) // can be speed up if arguments is an array in the first place - result[PARAMS_VARARGS_NAME] = $B.fast_tuple( Array.prototype.slice.call(args, PARAMS_POS_COUNT, HAS_KW ? -1 : undefined ) ); + result[PARAMS_VARARGS_NAME] = $B.fast_tuple( Array.prototype.slice.call(args, PARAMS_POS_COUNT, ARGS_POS_COUNT ) ); //result[PARAMS_VARARGS_NAME] = $B.fast_tuple( args.slice( PARAMS_POS_COUNT, HAS_KW ? -1 : undefined ) ); // maybe there is a faster way to build a tuple from a subset of an array. else if( ARGS_POS_COUNT > PARAMS_POS_COUNT ) { From ca4ad4ba0217c9111594ec32d465fc3f826d95e6 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 20:27:30 +0200 Subject: [PATCH 04/11] Remove some useless ?. --- www/src/py_utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index bb5ee1e3a..f77cd2ac4 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -197,7 +197,7 @@ function args0_NEW(fct, args) { // Handle defaults value for named parameters. // Optimize: precompute the number of named parameters with a default value, or just a boolean ? - const kwargs_defaults= $INFOS.__kwdefaults__?.$jsobj ?? $INFOS.__kwdefaults__?.$strings ?? {}; // costs a little... + let kwargs_defaults= $INFOS.__kwdefaults__.$jsobj ?? $INFOS.__kwdefaults__.$strings ?? {}; // costs a little... const named_default_keys = Object.keys(kwargs_defaults); // this operation is costly... if( named_default_keys.length < PARAMS_NAMED_COUNT ) { args0(fct, args); @@ -214,7 +214,7 @@ function args0_NEW(fct, args) { return result; } - const kwargs_defaults= $INFOS.__kwdefaults__?.$jsobj ?? $INFOS.__kwdefaults__?.$strings ?? {}; // costs a little... + const kwargs_defaults= $INFOS.__kwdefaults__.$jsobj ?? $INFOS.__kwdefaults__.$strings ?? {}; // costs a little... // Construct the list of default values... // Optimize : I'd need an object containing ALL default values instead of having to build one... From fccdd3af9896cdf26faff99c9f17cc0df895fcc0 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 20:44:40 +0200 Subject: [PATCH 05/11] Replace ?? by conditions --- www/src/py_utils.js | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index f77cd2ac4..ca60c8c43 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -197,7 +197,14 @@ function args0_NEW(fct, args) { // Handle defaults value for named parameters. // Optimize: precompute the number of named parameters with a default value, or just a boolean ? - let kwargs_defaults= $INFOS.__kwdefaults__.$jsobj ?? $INFOS.__kwdefaults__.$strings ?? {}; // costs a little... + let kwargs_defaults = $INFOS.__kwdefaults__.$jsobj; + if( kwargs_defaults === undefined || kwargs_defaults === null ) { + + kwargs_defaults = $INFOS.__kwdefaults__.$strings; + if( kwargs_defaults === undefined || kwargs_defaults === null ) + throw new Error('Named argument expected (args0 should have raised an error) !'); + } + const named_default_keys = Object.keys(kwargs_defaults); // this operation is costly... if( named_default_keys.length < PARAMS_NAMED_COUNT ) { args0(fct, args); @@ -214,7 +221,14 @@ function args0_NEW(fct, args) { return result; } - const kwargs_defaults= $INFOS.__kwdefaults__.$jsobj ?? $INFOS.__kwdefaults__.$strings ?? {}; // costs a little... + let kwargs_defaults = $INFOS.__kwdefaults__.$jsobj; + if( kwargs_defaults === undefined || kwargs_defaults == null ) { + + kwargs_defaults = $INFOS.__kwdefaults__.$strings; + if( kwargs_defaults === undefined || kwargs_defaults == null ) + kwargs_defaults = {} + } + //const kwargs_defaults= $INFOS.__kwdefaults__.$jsobj ?? $INFOS.__kwdefaults__.$strings ?? {}; // costs a little... // Construct the list of default values... // Optimize : I'd need an object containing ALL default values instead of having to build one... @@ -250,7 +264,12 @@ function args0_NEW(fct, args) { for(let id = 0; id < ARGS_NAMED.length; ++id ) { const _kargs = ARGS_NAMED[id] - const kargs = _kargs.$jsobj ?? _kargs.$strings ?? _kargs; // I don't think I can do better. + let kargs = _kargs.$jsobj; + if( kargs === undefined || kargs === null) { + kargs = _kargs.$strings + if(kargs === undefined || kargs === null) + kargs= _kargs; // I don't think I can do better. + } const keys = Object.keys(kargs); nb_named_args += keys.length; @@ -303,7 +322,12 @@ function args0_NEW(fct, args) { for(let id = 0; id < ARGS_NAMED.length; ++id ) { const _kargs = ARGS_NAMED[id] - const kargs = _kargs.$jsobj ?? _kargs.$strings ?? _kargs; + let kargs = _kargs.$jsobj; + if( kargs === undefined || kargs === null) { + kargs = _kargs.$strings + if(kargs === undefined || kargs === null) + kargs= _kargs; // I don't think I can do better. + } const keys = Object.keys(kargs); for(let k = 0; k < keys.length; ++k) { From 3bf4a2e712efbcbdadde4e12c89c832d06d73858 Mon Sep 17 00:00:00 2001 From: denmigda Date: Tue, 24 Oct 2023 22:29:53 +0200 Subject: [PATCH 06/11] Avoid copy of default named parameters --- www/src/py_utils.js | 51 ++++++++++++++++++++++++++++++--------------- 1 file changed, 34 insertions(+), 17 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index ca60c8c43..14a6a39af 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -234,12 +234,14 @@ function args0_NEW(fct, args) { // Optimize : I'd need an object containing ALL default values instead of having to build one... // If not done, I can work on it to remove the construction of this object (which cost a little). // Note: I should exclude posonly default parameters... (we don't need them as remaining positional only parameters'd be consumed next) - const kargs_defaults= { ... kwargs_defaults } //costly + /* const kargs_defaults= { ... kwargs_defaults } //costly for(let i = 0; i < PARAMS_POS_DEFAULTS_COUNT; ++i) // costly - kargs_defaults[PARAMS_NAMES[PARAMS_POS_DEFAULTS_OFFSET+i]] = PARAMS_POS_DEFAULTS[i]; - + kargs_defaults[PARAMS_NAMES[PARAMS_POS_DEFAULTS_OFFSET+i]] = PARAMS_POS_DEFAULTS[i]; */ // Consume remaining positional only parameters (no positional arguments given, so expect default value). const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; + const PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + PARAMS_POS_DEFAULTS_OFFSET; + + if( offset < PARAMS_POSONLY_COUNT ) { if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { @@ -289,14 +291,22 @@ function args0_NEW(fct, args) { const key = PARAMS_NAMES[ioffset]; if( key in result ) continue; + + if( ioffset >= PARAMS_POS_DEFAULTS_OFFSET && ioffset < PARAMS_POS_DEFAULTS_MAXID ) { + + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; + ++found; + + } else { - if( ! (key in kargs_defaults) ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - - result[key] = kargs_defaults[key]; - ++found; + if( ! (key in kwargs_defaults) ) { + console.log(key, kwargs_defaults, PARAMS_POS_DEFAULTS); + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + result[key] = kwargs_defaults[key]; + ++found; + } } // PARAMS_NAMES.length - offset = the number of expected named arguments. @@ -355,13 +365,20 @@ function args0_NEW(fct, args) { if( key in result ) continue; - if( ! (key in kargs_defaults) ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } + if( ioffset >= PARAMS_POS_DEFAULTS_OFFSET && ioffset < PARAMS_POS_DEFAULTS_MAXID ) { + + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; + ++found; + + } else { - result[key] = kargs_defaults[key]; - ++found; + if( ! (key in kwargs_defaults) ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + result[key] = kwargs_defaults[key]; + ++found; + } } // Same as "No **kwargs parameter". @@ -382,7 +399,7 @@ function args0_NEW(fct, args) { } result[PARAMS_KWARGS_NAME] = __BRYTHON__.obj_dict(extra); - + return result; } From c11337d32658e99e2074fb009a0f5f38d40a2c84 Mon Sep 17 00:00:00 2001 From: denmigda Date: Wed, 25 Oct 2023 10:46:53 +0200 Subject: [PATCH 07/11] replace keys() by values() in named defaults when pos only args --- www/src/py_utils.js | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index 14a6a39af..c24b07cab 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -162,7 +162,7 @@ function args0_NEW(fct, args) { // process positional arguments => vargargs parameters... if( PARAMS_VARARGS_NAME !== null ) // can be speed up if arguments is an array in the first place - result[PARAMS_VARARGS_NAME] = $B.fast_tuple( Array.prototype.slice.call(args, PARAMS_POS_COUNT, ARGS_POS_COUNT ) ); + result[PARAMS_VARARGS_NAME] = $B.fast_tuple( Array.prototype.slice.call(args, PARAMS_POS_COUNT, ARGS_POS_COUNT ) ); //TODO: opti, better way to construct tuple from subarray ? //result[PARAMS_VARARGS_NAME] = $B.fast_tuple( args.slice( PARAMS_POS_COUNT, HAS_KW ? -1 : undefined ) ); // maybe there is a faster way to build a tuple from a subset of an array. else if( ARGS_POS_COUNT > PARAMS_POS_COUNT ) { @@ -201,22 +201,22 @@ function args0_NEW(fct, args) { if( kwargs_defaults === undefined || kwargs_defaults === null ) { kwargs_defaults = $INFOS.__kwdefaults__.$strings; - if( kwargs_defaults === undefined || kwargs_defaults === null ) + if( kwargs_defaults === undefined || kwargs_defaults === null ){ + args0(fct, args); throw new Error('Named argument expected (args0 should have raised an error) !'); + } } - const named_default_keys = Object.keys(kwargs_defaults); // this operation is costly... - if( named_default_keys.length < PARAMS_NAMED_COUNT ) { + const named_default_values = Object.values(kwargs_defaults); // TODO: precompute this plz. + const nb_named_defaults = named_default_values.length; + + if( nb_named_defaults < PARAMS_NAMED_COUNT ) { args0(fct, args); throw new Error('Named argument expected (args0 should have raised an error) !'); } - // The loop is a little costly... - // Test if using an array of name and values is faster than using an object... - for(let i = 0; i < named_default_keys.length; ++i) { - const key = named_default_keys[i]; - result[key] = kwargs_defaults[key]; - } + for(let i = 0; i < nb_named_defaults; ++i) + result[ PARAMS_NAMES[offset++] ] = named_default_values[i]; return result; } @@ -237,11 +237,12 @@ function args0_NEW(fct, args) { /* const kargs_defaults= { ... kwargs_defaults } //costly for(let i = 0; i < PARAMS_POS_DEFAULTS_COUNT; ++i) // costly kargs_defaults[PARAMS_NAMES[PARAMS_POS_DEFAULTS_OFFSET+i]] = PARAMS_POS_DEFAULTS[i]; */ + + // Consume remaining positional only parameters (no positional arguments given, so expect default value). const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; const PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + PARAMS_POS_DEFAULTS_OFFSET; - if( offset < PARAMS_POSONLY_COUNT ) { if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { @@ -276,7 +277,7 @@ function args0_NEW(fct, args) { const keys = Object.keys(kargs); nb_named_args += keys.length; - for(let k = 0; k < keys.length; ++k) { + for(let k = 0; k < keys.length; ++k) { //TODO: for in = quicker... [x2] const argname = keys[k]; result[ argname ] = kargs[argname]; @@ -299,8 +300,7 @@ function args0_NEW(fct, args) { } else { - if( ! (key in kwargs_defaults) ) { - console.log(key, kwargs_defaults, PARAMS_POS_DEFAULTS); + if( ! (key in kwargs_defaults) ) { // TODO values (play with indexes) + split in 4 loops ? args0(fct, args); throw new Error('Missing a named arguments (args0 should have raised an error) !'); } @@ -340,7 +340,7 @@ function args0_NEW(fct, args) { } const keys = Object.keys(kargs); - for(let k = 0; k < keys.length; ++k) { + for(let k = 0; k < keys.length; ++k) { //TODO for in const argname = keys[k]; @@ -372,7 +372,7 @@ function args0_NEW(fct, args) { } else { - if( ! (key in kwargs_defaults) ) { + if( ! (key in kwargs_defaults) ) { //TODO: values args0(fct, args); throw new Error('Missing a named arguments (args0 should have raised an error) !'); } From 7cddaa548bcffa380ca01f06a5a09b327e5f2db5 Mon Sep 17 00:00:00 2001 From: denmigda Date: Wed, 25 Oct 2023 11:07:48 +0200 Subject: [PATCH 08/11] Replace .keys() to for in when looping on named args --- www/src/py_utils.js | 25 +++++++++---------------- 1 file changed, 9 insertions(+), 16 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index c24b07cab..f22a2037b 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -274,14 +274,10 @@ function args0_NEW(fct, args) { kargs= _kargs; // I don't think I can do better. } - const keys = Object.keys(kargs); - nb_named_args += keys.length; - - for(let k = 0; k < keys.length; ++k) { //TODO: for in = quicker... [x2] - - const argname = keys[k]; - result[ argname ] = kargs[argname]; - } + for(let argname in kargs) { + result[ argname ] = kargs[argname]; + ++nb_named_args; + } } // Checks default values... @@ -338,21 +334,18 @@ function args0_NEW(fct, args) { if(kargs === undefined || kargs === null) kargs= _kargs; // I don't think I can do better. } - const keys = Object.keys(kargs); - - for(let k = 0; k < keys.length; ++k) { //TODO for in - - const argname = keys[k]; - // We search the name starting from non-positional only parameters. - if( PARAMS_NAMES.indexOf(argname, PARAMS_POSONLY_COUNT) !== -1 ) { + + for(let argname in kargs) { + + if( PARAMS_NAMES.indexOf(argname, PARAMS_POSONLY_COUNT) !== -1 ) { result[ argname ] = kargs[argname]; ++nb_named_args; } else { extra[ argname ] = kargs[argname]; ++nb_extra_args; } - } + } } // Same as "No **kwargs parameter". From 9f667ba3c3ae91e75c4f912b2a24bcb74b11bc4a Mon Sep 17 00:00:00 2001 From: denmigda Date: Wed, 25 Oct 2023 12:15:21 +0200 Subject: [PATCH 09/11] Replace in by .values() when searching for default values --- www/src/py_utils.js | 63 ++++++++++++++++++++++++++++----------------- 1 file changed, 39 insertions(+), 24 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index f22a2037b..89823accd 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -240,8 +240,11 @@ function args0_NEW(fct, args) { // Consume remaining positional only parameters (no positional arguments given, so expect default value). - const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; - const PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + PARAMS_POS_DEFAULTS_OFFSET; + const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; + const PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + PARAMS_POS_DEFAULTS_OFFSET; + + const PARAMS_NAMED_DEFAULTS = Object.values(kwargs_defaults); //TODO: precompute this plz + const PARAMS_NAMED_DEFAULTS_OFFSET = PARAMS_NAMES.length - PARAMS_NAMED_DEFAULTS.length; if( offset < PARAMS_POSONLY_COUNT ) { @@ -289,19 +292,25 @@ function args0_NEW(fct, args) { if( key in result ) continue; - if( ioffset >= PARAMS_POS_DEFAULTS_OFFSET && ioffset < PARAMS_POS_DEFAULTS_MAXID ) { - - result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; + if( ioffset < PARAMS_POS_DEFAULTS_MAXID) { + + if( ioffset < PARAMS_POS_DEFAULTS_OFFSET ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; ++found; - + } else { - - if( ! (key in kwargs_defaults) ) { // TODO values (play with indexes) + split in 4 loops ? - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - result[key] = kwargs_defaults[key]; - ++found; + + if( ioffset < PARAMS_NAMED_DEFAULTS_OFFSET ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = PARAMS_NAMED_DEFAULTS[PARAMS_NAMED_DEFAULTS_OFFSET - ioffset]; // should be quicker + ++found; } } @@ -358,20 +367,26 @@ function args0_NEW(fct, args) { if( key in result ) continue; - if( ioffset >= PARAMS_POS_DEFAULTS_OFFSET && ioffset < PARAMS_POS_DEFAULTS_MAXID ) { - - result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; - ++found; - - } else { - - if( ! (key in kwargs_defaults) ) { //TODO: values - args0(fct, args); + if( ioffset < PARAMS_POS_DEFAULTS_MAXID) { + + if( ioffset < PARAMS_POS_DEFAULTS_OFFSET ) { + args0(fct, args); throw new Error('Missing a named arguments (args0 should have raised an error) !'); } - result[key] = kwargs_defaults[key]; + + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; ++found; - } + + } else { + + if( ioffset < PARAMS_NAMED_DEFAULTS_OFFSET ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = PARAMS_NAMED_DEFAULTS[PARAMS_NAMED_DEFAULTS_OFFSET - ioffset]; // should be quicker + ++found; + } } // Same as "No **kwargs parameter". From 7459d76560f3bb98d755f9d1fa9ae42508dfb5c4 Mon Sep 17 00:00:00 2001 From: denmigda Date: Wed, 25 Oct 2023 12:51:22 +0200 Subject: [PATCH 10/11] Replace in by .values() when searching for default values --- www/src/py_utils.js | 114 +++++++++++++++++++++++++------------------- 1 file changed, 66 insertions(+), 48 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index 89823accd..bec574553 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -264,6 +264,7 @@ function args0_NEW(fct, args) { // No **kwargs parameter (i.e. unknown name = error). if( PARAMS_KWARGS_NAME === null ) { + let nb_named_args = 0; @@ -283,35 +284,43 @@ function args0_NEW(fct, args) { } } - // Checks default values... - // What is quicker ? An object or 1 array of name (with indexOf) and 1 array of values ? let found = 0; - for(let ioffset = offset; ioffset < PARAMS_NAMES.length; ++ioffset) { - + let ioffset = offset; + for( ; ioffset < PARAMS_POS_DEFAULTS_OFFSET; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) // maybe could be speed up using "!(key in result)" + continue; + + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + for( ; ioffset < PARAMS_POS_DEFAULTS_MAXID; ++ioffset) { + const key = PARAMS_NAMES[ioffset]; if( key in result ) continue; - if( ioffset < PARAMS_POS_DEFAULTS_MAXID) { - - if( ioffset < PARAMS_POS_DEFAULTS_OFFSET ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - - result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; - ++found; - - } else { - - if( ioffset < PARAMS_NAMED_DEFAULTS_OFFSET ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - - result[key] = PARAMS_NAMED_DEFAULTS[PARAMS_NAMED_DEFAULTS_OFFSET - ioffset]; // should be quicker - ++found; - } + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; + ++found; + } + for( ; ioffset < PARAMS_NAMED_DEFAULTS_OFFSET; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) // maybe could be speed up using "!(key in result)" + continue; + + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + for( ; ioffset < PARAMS_NAMES.length; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) + continue; + + result[key] = PARAMS_NAMED_DEFAULTS[ioffset - PARAMS_NAMED_DEFAULTS_OFFSET]; // should be quicker + ++found; } // PARAMS_NAMES.length - offset = the number of expected named arguments. @@ -361,32 +370,41 @@ function args0_NEW(fct, args) { // Checks default values... // What is quicker ? An object or 1 array of name (with indexOf) and 1 array of values ? let found = 0; - for(let ioffset = offset; ioffset < PARAMS_NAMES.length; ++ioffset) { - + let ioffset = offset; + for( ; ioffset < PARAMS_POS_DEFAULTS_OFFSET; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) // maybe could be speed up using "!(key in result)" + continue; + + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + for( ; ioffset < PARAMS_POS_DEFAULTS_MAXID; ++ioffset) { + const key = PARAMS_NAMES[ioffset]; if( key in result ) - continue; - - if( ioffset < PARAMS_POS_DEFAULTS_MAXID) { - - if( ioffset < PARAMS_POS_DEFAULTS_OFFSET ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - - result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; - ++found; - - } else { - - if( ioffset < PARAMS_NAMED_DEFAULTS_OFFSET ) { - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } - - result[key] = PARAMS_NAMED_DEFAULTS[PARAMS_NAMED_DEFAULTS_OFFSET - ioffset]; // should be quicker - ++found; - } + continue; + + result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; + ++found; + } + for( ; ioffset < PARAMS_NAMED_DEFAULTS_OFFSET; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) // maybe could be speed up using "!(key in result)" + continue; + + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + for( ; ioffset < PARAMS_NAMES.length; ++ioffset) { + + const key = PARAMS_NAMES[ioffset]; + if( key in result ) + continue; + result[key] = PARAMS_NAMED_DEFAULTS[ioffset - PARAMS_NAMED_DEFAULTS_OFFSET]; // should be quicker + ++found; } // Same as "No **kwargs parameter". From 4496ff369e171c786ac68910bbb020aa9a44a56a Mon Sep 17 00:00:00 2001 From: denmigda Date: Wed, 25 Oct 2023 13:57:52 +0200 Subject: [PATCH 11/11] Version that works --- www/src/py_utils.js | 38 ++++++++++++++------------------------ 1 file changed, 14 insertions(+), 24 deletions(-) diff --git a/www/src/py_utils.js b/www/src/py_utils.js index bec574553..fb0eec109 100644 --- a/www/src/py_utils.js +++ b/www/src/py_utils.js @@ -243,9 +243,6 @@ function args0_NEW(fct, args) { const PARAMS_POSONLY_COUNT = $CODE.co_posonlyargcount; const PARAMS_POS_DEFAULTS_MAXID = PARAMS_POS_DEFAULTS_COUNT + PARAMS_POS_DEFAULTS_OFFSET; - const PARAMS_NAMED_DEFAULTS = Object.values(kwargs_defaults); //TODO: precompute this plz - const PARAMS_NAMED_DEFAULTS_OFFSET = PARAMS_NAMES.length - PARAMS_NAMED_DEFAULTS.length; - if( offset < PARAMS_POSONLY_COUNT ) { if( offset < PARAMS_POS_DEFAULTS_OFFSET ) { @@ -304,22 +301,18 @@ function args0_NEW(fct, args) { result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; ++found; } - for( ; ioffset < PARAMS_NAMED_DEFAULTS_OFFSET; ++ioffset) { - - const key = PARAMS_NAMES[ioffset]; - if( key in result ) // maybe could be speed up using "!(key in result)" - continue; - - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } for( ; ioffset < PARAMS_NAMES.length; ++ioffset) { const key = PARAMS_NAMES[ioffset]; if( key in result ) continue; - result[key] = PARAMS_NAMED_DEFAULTS[ioffset - PARAMS_NAMED_DEFAULTS_OFFSET]; // should be quicker + if( ! (key in kwargs_defaults) ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = kwargs_defaults[key]; ++found; } @@ -389,22 +382,19 @@ function args0_NEW(fct, args) { result[key] = PARAMS_POS_DEFAULTS[ioffset - PARAMS_POS_DEFAULTS_OFFSET]; ++found; } - for( ; ioffset < PARAMS_NAMED_DEFAULTS_OFFSET; ++ioffset) { - - const key = PARAMS_NAMES[ioffset]; - if( key in result ) // maybe could be speed up using "!(key in result)" - continue; - - args0(fct, args); - throw new Error('Missing a named arguments (args0 should have raised an error) !'); - } for( ; ioffset < PARAMS_NAMES.length; ++ioffset) { const key = PARAMS_NAMES[ioffset]; if( key in result ) continue; - result[key] = PARAMS_NAMED_DEFAULTS[ioffset - PARAMS_NAMED_DEFAULTS_OFFSET]; // should be quicker - ++found; + + if( ! (key in kwargs_defaults) ) { + args0(fct, args); + throw new Error('Missing a named arguments (args0 should have raised an error) !'); + } + + result[key] = kwargs_defaults[key]; + ++found; } // Same as "No **kwargs parameter".