Many hyperlinks are disabled.
Use anonymous login
to enable hyperlinks.
Overview
Comment: | Experimentally added the __using() function-like s2 keyword. |
---|---|
Downloads: | Tarball | ZIP archive | SQL archive |
Timelines: | family | ancestors | descendants | both | trunk |
Files: | files | file ages | folders |
SHA1: |
a009b47e67e1e5dbc78a7b4dcad97725 |
User & Date: | stephan 2019-12-09 09:09:45 |
Context
2019-12-09
| ||
11:07 | Removed CMakeLists.txt, as it's long-since unmaintained. check-in: d4cc06f255 user: stephan tags: trunk | |
09:09 | Experimentally added the __using() function-like s2 keyword. check-in: a009b47e67 user: stephan tags: trunk | |
2019-12-08
| ||
18:35 | Added Fossil Delta (felta) s2 module because... not yet certain. check-in: ebd44c8be5 user: stephan tags: trunk | |
Changes
Changes to s2/s2.h.
︙ | ︙ | |||
959 960 961 962 963 964 965 966 967 968 969 970 971 972 | /** An experiment. 20160125: reminder to self: the next time i write that without an explanation for my future self to understand, please flog me. Currently trying to figure out what this is for an am at a loss. It doesn't appear to do anything useful. */ s2_func_state const * currentScriptFunc; /** Various commonly-used values which the engine stashes away for its own use... */ | > > > | 959 960 961 962 963 964 965 966 967 968 969 970 971 972 973 974 975 | /** An experiment. 20160125: reminder to self: the next time i write that without an explanation for my future self to understand, please flog me. Currently trying to figure out what this is for an am at a loss. It doesn't appear to do anything useful. 20191209: used for implementing the experimental __using keyword to access "using" properties from inside a script function. */ s2_func_state const * currentScriptFunc; /** Various commonly-used values which the engine stashes away for its own use... */ |
︙ | ︙ |
Changes to s2/s2_eval.c.
︙ | ︙ | |||
323 324 325 326 327 328 329 330 331 332 333 334 335 336 | static int s2_keyword_f_nameof( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_new( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_refcount( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_reserved( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_typeinfo( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_typename( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_unset( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_var( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_while( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static const struct S2_KEYWORDS__ { /* Keep sorted on keyword name (string) so that we can binary-search it. | > | 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 | static int s2_keyword_f_nameof( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_new( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_refcount( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_reserved( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_typeinfo( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_typename( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_unset( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_using( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_var( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static int s2_keyword_f_while( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv); static const struct S2_KEYWORDS__ { /* Keep sorted on keyword name (string) so that we can binary-search it. |
︙ | ︙ | |||
350 351 352 353 354 355 356 357 358 359 360 361 362 363 | */ s2_keyword const _breakpoint; s2_keyword const _col; s2_keyword const _file; s2_keyword const _filedir; s2_keyword const _flc; s2_keyword const _line; s2_keyword const affirm; s2_keyword const assert_; s2_keyword const break_; s2_keyword const catch_; s2_keyword const class_; s2_keyword const const_; s2_keyword const continue_; | > | 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 | */ s2_keyword const _breakpoint; s2_keyword const _col; s2_keyword const _file; s2_keyword const _filedir; s2_keyword const _flc; s2_keyword const _line; s2_keyword const _using; s2_keyword const affirm; s2_keyword const assert_; s2_keyword const break_; s2_keyword const catch_; s2_keyword const class_; s2_keyword const const_; s2_keyword const continue_; |
︙ | ︙ | |||
399 400 401 402 403 404 405 | s2_keyword const typeName; s2_keyword const undef_; s2_keyword const unset; s2_keyword const var; s2_keyword const while_; s2_keyword const _sentinel_; } S2_KWDS = { | | > | 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 | s2_keyword const typeName; s2_keyword const undef_; s2_keyword const unset; s2_keyword const var; s2_keyword const while_; s2_keyword const _sentinel_; } S2_KWDS = { /*{ id word, wordLen, call(), allowEOLAsEOXWhenLHS } */ { S2_T_KeywordBREAKPOINT, "__BREAKPOINT", 12, s2_keyword_f_breakpoint, 0 }, { S2_T_KeywordCOLUMN, "__COLUMN", 8, s2_keyword_f_FLC, 0 }, { S2_T_KeywordFILE, "__FILE", 6, s2_keyword_f_FLC, 0 }, { S2_T_KeywordFILEDIR, "__FILEDIR", 9, s2_keyword_f_FLC, 0 }, { S2_T_KeywordSRCPOS, "__FLC", 5, s2_keyword_f_FLC, 0 }, { S2_T_KeywordLINE, "__LINE", 6, s2_keyword_f_FLC, 0 }, { S2_T_Keyword__Using, "__using", 7, s2_keyword_f_using, 0 }, { S2_T_KeywordAffirm, "affirm", 6, s2_keyword_f_assert, 0 }, { S2_T_KeywordAssert, "assert", 6, s2_keyword_f_assert, 0 }, { S2_T_KeywordBreak, "break", 5, s2_keyword_f_eval, 0 }, { S2_T_KeywordCatch, "catch", 5, s2_keyword_f_eval, 0 }, { S2_T_KeywordClass, "class", 5, s2_keyword_f_reserved, 0 }, { S2_T_KeywordConst, "const", 5, s2_keyword_f_var, 0 }, |
︙ | ︙ | |||
535 536 537 538 539 540 541 542 543 544 545 546 547 548 | WORD(S2_T_KeywordThrow,throw_); WORD(S2_T_KeywordTrue,true_); WORD(S2_T_KeywordTry,try_); WORD(S2_T_KeywordTypeinfo,typeInfo); WORD(S2_T_KeywordTypename,typeName); WORD(S2_T_KeywordUndefined,undef_); WORD(S2_T_KeywordUnset,unset); WORD(S2_T_KeywordVar,var); WORD(S2_T_KeywordWhile,while_); #undef WORD default: return 0; } | > | 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 | WORD(S2_T_KeywordThrow,throw_); WORD(S2_T_KeywordTrue,true_); WORD(S2_T_KeywordTry,try_); WORD(S2_T_KeywordTypeinfo,typeInfo); WORD(S2_T_KeywordTypename,typeName); WORD(S2_T_KeywordUndefined,undef_); WORD(S2_T_KeywordUnset,unset); WORD(S2_T_Keyword__Using,_using); WORD(S2_T_KeywordVar,var); WORD(S2_T_KeywordWhile,while_); #undef WORD default: return 0; } |
︙ | ︙ | |||
577 578 579 580 581 582 583 584 585 586 587 588 589 590 | #define WORD(W) &S2_KWDS.W WORD(_breakpoint), WORD(_col), WORD(_file), WORD(_filedir), WORD(_flc), WORD(_line), WORD(affirm), WORD(assert_), WORD(break_), WORD(catch_), WORD(class_), WORD(const_), WORD(continue_), | > | 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 | #define WORD(W) &S2_KWDS.W WORD(_breakpoint), WORD(_col), WORD(_file), WORD(_filedir), WORD(_flc), WORD(_line), WORD(_using), WORD(affirm), WORD(assert_), WORD(break_), WORD(catch_), WORD(class_), WORD(const_), WORD(continue_), |
︙ | ︙ | |||
947 948 949 950 951 952 953 | If asExpr is true it uses s2_eval_expr_impl() to process one expression, otherwise it uses s2_eval_ptoker() to parse it all as a collection of expressions. */ static int s2_eval_current_token( s2_engine * se, s2_ptoker * pr, | | | | | 952 953 954 955 956 957 958 959 960 961 962 963 964 965 966 967 968 | If asExpr is true it uses s2_eval_expr_impl() to process one expression, otherwise it uses s2_eval_ptoker() to parse it all as a collection of expressions. */ static int s2_eval_current_token( s2_engine * se, s2_ptoker * pr, char asExpr, int evalFlags, cwal_value **rv ){ int rc = s2_check_interrupted(se, 0); pr->errToken = s2_ptoken_empty; /* assert(!name || nameLen>0); */ if(rc) return rc; else if(!s2_ptoken_has_content(&pr->token)){ /* we know it's empty, so don't bother.*/ if(rv) *rv = 0 |
︙ | ︙ | |||
2121 2122 2123 2124 2125 2126 2127 | : "<NULL>"); goto end; } /* MARKER(("Function call args: %.*s\n", (int)(pr->token.end-pr->token.begin), pr->token.begin)); */ /* s2_dump_val(vSelf,"call() vSelf"); */ /* s2_dump_val(fv,"call() func"); */ | < | 2126 2127 2128 2129 2130 2131 2132 2133 2134 2135 2136 2137 2138 2139 | : "<NULL>"); goto end; } /* MARKER(("Function call args: %.*s\n", (int)(pr->token.end-pr->token.begin), pr->token.begin)); */ /* s2_dump_val(vSelf,"call() vSelf"); */ /* s2_dump_val(fv,"call() func"); */ fst = s2_func_state_for_func(theFunc); { /* Process the args and call() the func... */ s2_ptoker sub = s2_ptoker_empty; cwal_value * arV = 0; cwal_array * oldArgV; s2_func_state const * oldScriptFunc = se->currentScriptFunc; |
︙ | ︙ | |||
2150 2151 2152 2153 2154 2155 2156 | if(rc){ cwal_value_make_vacuum_proof(arV, 0); cwal_value_unref(arV); goto end; } oldArgV = se->callArgV; se->callArgV = 0; | | > > | 2154 2155 2156 2157 2158 2159 2160 2161 2162 2163 2164 2165 2166 2167 2168 2169 2170 | if(rc){ cwal_value_make_vacuum_proof(arV, 0); cwal_value_unref(arV); goto end; } oldArgV = se->callArgV; se->callArgV = 0; se->currentScriptFunc = 0 /* if this is a script-side function call, s2_callback_f(), which is about to trigger, will set this to the corresponding function. */; pr->errToken = origin /* just in case, location for error reporting */; if(0){ MARKER(("Function call()...\n")); s2_dump_val(vSelf ? vSelf : fv, "vSelf ? vSelf : fv"); s2_dump_val(cwal_array_value(ar), "argv"); } |
︙ | ︙ | |||
2195 2196 2197 2198 2199 2200 2201 | vSelf ? vSelf : (1 ? fv : cwal_value_undefined()), &xrv, ar ) /* Still wavering on this point: when called without a propery access (vSelf===0), should the function's "this" be the undefined value or itself? i don't want "this" propagating in | | | | | 2201 2202 2203 2204 2205 2206 2207 2208 2209 2210 2211 2212 2213 2214 2215 2216 2217 2218 2219 2220 2221 2222 2223 2224 2225 2226 | vSelf ? vSelf : (1 ? fv : cwal_value_undefined()), &xrv, ar ) /* Still wavering on this point: when called without a propery access (vSelf===0), should the function's "this" be the undefined value or itself? i don't want "this" propagating in from an older scope. One argument for passing fv instead of undefined: var obj = { prototype: proc(x){ this.x = argv.x } }; obj(1); 'this' is undefined in that call. Another argument: it seems that i already wrote a chapter on how to use/abuse it in the docs. */; } se->currentScriptFunc = oldScriptFunc; /* assert(!se->callArgV && "Gets unset via the post-call() hook"); interesting problem: on certain types of errors, first made possible with the introduction of interceptors, the pre() |
︙ | ︙ | |||
2231 2232 2233 2234 2235 2236 2237 | /* we have to ref *rv before destroying arV for the case that *rv==arV or arV contains (perhaps indirectly) *rv. */ cwal_value_ref(xrv); } cwal_value_unref(arV); } end: | < | | > | | 2237 2238 2239 2240 2241 2242 2243 2244 2245 2246 2247 2248 2249 2250 2251 2252 2253 2254 2255 2256 2257 2258 2259 2260 2261 2262 2263 2264 2265 2266 2267 2268 2269 2270 2271 2272 2273 2274 | /* we have to ref *rv before destroying arV for the case that *rv==arV or arV contains (perhaps indirectly) *rv. */ cwal_value_ref(xrv); } cwal_value_unref(arV); } end: rc = s2_check_interrupted(se, rc); switch(rc){ case 0: break; case CWAL_RC_EXCEPTION: s2_exception_add_script_props(se, pr) /* Needed to decorate calls to native functions. */; CWAL_SWITCH_FALL_THROUGH; /* case CWAL_SCR_SYNTAX: */ case CWAL_RC_OOM: case CWAL_RC_EXIT: case CWAL_RC_FATAL: case CWAL_RC_INTERRUPTED: case CWAL_RC_ASSERT: /* do we want assertion errors to translate to exceptions if they go up the call stack? Much later: no.*/ break; case CWAL_RC_BREAK: case CWAL_RC_CONTINUE: /* If a break/continue are triggered while handling the arguments, that's okay: let them propagate so they can be caught by upstream code. If they passed through the call() itself, that's not okay: stop them from propagating further. */ if(!reachedTheCall) break; CWAL_SWITCH_FALL_THROUGH; default: if(se->err.code){ /* Likely a syntax-related error pending */ /*MARKER(("[rc=%s] se->err.msg=%s\n", s2_rc_cstr(se->err.code), (char *)se->err.msg.mem));*/ |
︙ | ︙ | |||
2359 2360 2361 2362 2363 2364 2365 | It's crashing in the && operator, and it seems to be a lifetime problem related to the 'd' value (possibly getting hosed via the 2nd assignment?). Running with recycling ON (-R instead of --R) hides the problem but potentially causes Weird Broken Stuff to happen later (as is being witnessed in the CGI plugin, which was where this was initially triggered). This is the first serious | | | | | 2365 2366 2367 2368 2369 2370 2371 2372 2373 2374 2375 2376 2377 2378 2379 2380 2381 2382 | It's crashing in the && operator, and it seems to be a lifetime problem related to the 'd' value (possibly getting hosed via the 2nd assignment?). Running with recycling ON (-R instead of --R) hides the problem but potentially causes Weird Broken Stuff to happen later (as is being witnessed in the CGI plugin, which was where this was initially triggered). This is the first serious lifetime-related bug we've seen the core in a couple years :`(. A decent solution, aside from the eval holder, is as yet unknown :`(. Other formulations and permutations: (Forewarning: these are often dependent on the exact recycling options used and the internal state of the recycling system(s)!) var d; if((d = s2.getenv('HOME')) && (d = s2.fs.realpath(d+'/fossil'))) 0; // ^^^ crashes |
︙ | ︙ | |||
2644 2645 2646 2647 2648 2649 2650 | # define eval_hold(V) if( holder && (V) \ && (rc = eval_hold_this_value(holder, (V)))) goto end #else # define eval_hold(V) /* now done via s2_process_op_impl(), and yet... that doesn't get all of them. */ #endif /* | | | 2650 2651 2652 2653 2654 2655 2656 2657 2658 2659 2660 2661 2662 2663 2664 | # define eval_hold(V) if( holder && (V) \ && (rc = eval_hold_this_value(holder, (V)))) goto end #else # define eval_hold(V) /* now done via s2_process_op_impl(), and yet... that doesn't get all of them. */ #endif /* Create se->scopes.current->evalHolder to hold refs to our being-eval'd values. As an optimization, if we're in skip mode, don't bother... and let's hope that this function is never called in such a way that skip mode is disabled by a downstream sub-eval (because this optimization will hose us if that happens). */ if(!se->skipLevel && !(holder = se->scopes.current->evalHolder)){ holder = cwal_new_array(se->e); |
︙ | ︙ | |||
4260 4261 4262 4263 4264 4265 4266 | } se->currentScript = oldScript; return rc; } int s2_keyword_f_breakpoint( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv){ int rc; | | > > | 4266 4267 4268 4269 4270 4271 4272 4273 4274 4275 4276 4277 4278 4279 4280 4281 4282 | } se->currentScript = oldScript; return rc; } int s2_keyword_f_breakpoint( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv){ int rc; S2_UNUSED_ARG kw; S2_UNUSED_ARG se; S2_UNUSED_ARG pr; *rv = cwal_value_undefined(); rc = 0 /* place breakpoint here */; /** TODO: add an interactive breakpoint callback hook, which the client can optionally start running when the hook is called. e.g. s2sh might |
︙ | ︙ | |||
4289 4290 4291 4292 4293 4294 4295 | switch(pr->token.ttype){ case S2_T_KeywordUndefined: *rv = cwal_value_undefined(); return 0; case S2_T_KeywordNull: *rv = cwal_value_null(); return 0; case S2_T_KeywordTrue: *rv = cwal_value_true(); return 0; case S2_T_KeywordFalse: *rv = cwal_value_false(); return 0; default: assert(!"Invalid keyword mapping"); | | | 4297 4298 4299 4300 4301 4302 4303 4304 4305 4306 4307 4308 4309 4310 4311 | switch(pr->token.ttype){ case S2_T_KeywordUndefined: *rv = cwal_value_undefined(); return 0; case S2_T_KeywordNull: *rv = cwal_value_null(); return 0; case S2_T_KeywordTrue: *rv = cwal_value_true(); return 0; case S2_T_KeywordFalse: *rv = cwal_value_false(); return 0; default: assert(!"Invalid keyword mapping"); S2_UNUSED_ARG kw; s2_fatal(CWAL_RC_RANGE, "Invalid keyword mapping in s2_keyword_f_builtin_vals()") /* does not return, but your compiler doesn't know that, so... */; return CWAL_RC_ERROR; } } } |
︙ | ︙ | |||
6655 6656 6657 6658 6659 6660 6661 6662 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 | assertion may be triggered (or a NULL pointer deref). */ int s2_callback_hook_pre(cwal_callback_args const * args, void * stateUnused){ s2_func_state * fs = args->callee ? s2_func_state_for_func(args->callee) : NULL; /* MARKER(("PRE-FUNC HOOK fstate=%p argc=%d!\n", (void const *)fs, (int)args->argc)); */ if( !fs /* It's a native function */ || (/* A function with neither body content nor params. */ fs->flags & S2_FUNCSTATE_F_EMPTY_BODY && fs->flags & S2_FUNCSTATE_F_EMPTY_PARAMS) ){ s2_engine * se = s2_engine_from_state(args->engine); se->callArgV = 0 /* Make sure this doesn't propagate through to a script func that this native function calls. Been there, debugged that! */; /* MARKER(("Non-script or empty function. Not injecting argv/this.\n")); */ s2_dotop_state( se, 0, 0, 0 ) /* necessary!!! */; return 0; } | > > | 6663 6664 6665 6666 6667 6668 6669 6670 6671 6672 6673 6674 6675 6676 6677 6678 6679 6680 6681 6682 6683 6684 6685 6686 | assertion may be triggered (or a NULL pointer deref). */ int s2_callback_hook_pre(cwal_callback_args const * args, void * stateUnused){ s2_func_state * fs = args->callee ? s2_func_state_for_func(args->callee) : NULL; S2_UNUSED_ARG stateUnused; /* MARKER(("PRE-FUNC HOOK fstate=%p argc=%d!\n", (void const *)fs, (int)args->argc)); */ if( !fs /* It's a native function */ || (/* A function with neither body content nor params. */ fs->flags & S2_FUNCSTATE_F_EMPTY_BODY && fs->flags & S2_FUNCSTATE_F_EMPTY_PARAMS) ){ s2_engine * se = s2_engine_from_state(args->engine); assert(se); se->callArgV = 0 /* Make sure this doesn't propagate through to a script func that this native function calls. Been there, debugged that! */; /* MARKER(("Non-script or empty function. Not injecting argv/this.\n")); */ s2_dotop_state( se, 0, 0, 0 ) /* necessary!!! */; return 0; } |
︙ | ︙ | |||
6767 6768 6769 6770 6771 6772 6773 | #endif if(fs){ /* Import imported properties (using/Function.importSymbols()) into the current scope. */ if(fs->vImported){ rc = cwal_scope_import_properties( s, fs->vImported ); | < | 6777 6778 6779 6780 6781 6782 6783 6784 6785 6786 6787 6788 6789 6790 | #endif if(fs){ /* Import imported properties (using/Function.importSymbols()) into the current scope. */ if(fs->vImported){ rc = cwal_scope_import_properties( s, fs->vImported ); } } return rc; } } int s2_callback_hook_post(cwal_callback_args const * args, |
︙ | ︙ | |||
6980 6981 6982 6983 6984 6985 6986 6987 6988 6989 6990 6991 6992 6993 | rc = s2_throw_ptoker(se, pr, CWAL_RC_TYPE, "Expecting an Object literal but got a %s.", cwal_value_type_name(v)); cwal_value_unref(v); } else if(!fst->vImported){ fst->vImported = v /* fst now owns the ref point */; s2_value_to_lhs_scope(fv, v); }else{ rc = cwal_props_copy(v, fst->vImported); cwal_value_unref(v); } v = 0; } | > | 6989 6990 6991 6992 6993 6994 6995 6996 6997 6998 6999 7000 7001 7002 7003 | rc = s2_throw_ptoker(se, pr, CWAL_RC_TYPE, "Expecting an Object literal but got a %s.", cwal_value_type_name(v)); cwal_value_unref(v); } else if(!fst->vImported){ fst->vImported = v /* fst now owns the ref point */; cwal_value_prototype_set(v, NULL); s2_value_to_lhs_scope(fv, v); }else{ rc = cwal_props_copy(v, fst->vImported); cwal_value_unref(v); } v = 0; } |
︙ | ︙ | |||
7912 7913 7914 7915 7916 7917 7918 | TYPEINFO_ISINT, /** True if operand is a tuple or an array. */ TYPEINFO_ISLIST, /** True if operand is a var/const declared in the local scope. */ TYPEINFO_ISLOCAL, /** True if operand is a native. */ TYPEINFO_ISNATIVE, | | | 7922 7923 7924 7925 7926 7927 7928 7929 7930 7931 7932 7933 7934 7935 7936 | TYPEINFO_ISINT, /** True if operand is a tuple or an array. */ TYPEINFO_ISLIST, /** True if operand is a var/const declared in the local scope. */ TYPEINFO_ISLOCAL, /** True if operand is a native. */ TYPEINFO_ISNATIVE, /** True if the current "this" is being initalized via the 'new' keyword. */ TYPEINFO_ISNEWING, /** True if operand is an integer or a double. */ TYPEINFO_ISNUMBER, /** True if operand is an integer, a double, a boolean, or a numeric-format string (one parseable by NumberPrototype.parseNumber()). |
︙ | ︙ | |||
9045 9046 9047 9048 9049 9050 9051 9052 9053 | if(operand){ if(!rc && rv) cwal_value_ref(*rv); cwal_value_unref(operand); if(!rc && rv) cwal_value_unhand(*rv); } return rc; } #undef MARKER | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 9055 9056 9057 9058 9059 9060 9061 9062 9063 9064 9065 9066 9067 9068 9069 9070 9071 9072 9073 9074 9075 9076 9077 9078 9079 9080 9081 9082 9083 9084 9085 9086 9087 9088 9089 9090 9091 9092 9093 9094 9095 9096 9097 9098 9099 9100 9101 9102 9103 9104 9105 9106 9107 9108 9109 9110 9111 9112 9113 9114 9115 9116 9117 9118 9119 9120 9121 9122 9123 9124 9125 9126 9127 9128 9129 | if(operand){ if(!rc && rv) cwal_value_ref(*rv); cwal_value_unref(operand); if(!rc && rv) cwal_value_unhand(*rv); } return rc; } /** The __using() function-like keyword is used for accessing "using"/importSymbols()-injected state. */ int s2_keyword_f_using( s2_keyword const * kw, s2_engine * se, s2_ptoker * pr, cwal_value **rv){ int rc; cwal_value * imports = 0; cwal_value * xrv = 0; assert(S2_T_Keyword__Using == kw->id); rc = s2_next_token(se, pr, 0, 0); if(rc) return rc; if(S2_T_ParenGroup!=pr->token.ttype){ return s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX, "Expecting (...) after %s keyword.", kw->word); } rc = s2_eval_current_token(se, pr, 0, S2_EVAL_PUSH_SCOPE, &xrv); if(rc){ assert(!xrv); return rc; } else if(!xrv){ /* __using() is valid in the current script-side func to refer to its own imports. */ if(!se->currentScriptFunc){ rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX, "%s() is only valid in the body of a " "script-implemented function.", kw->word); }else{ imports = se->currentScriptFunc->vImported; cwal_value_ref(imports); } }else{ /* __using(expr), where expr must be a script-side function. */ cwal_function * f = cwal_value_get_function(xrv); s2_func_state * fst = f ? s2_func_state_for_func(f) : 0; cwal_value_ref(xrv); if(!fst){ rc = s2_throw_ptoker(se, pr, CWAL_RC_TYPE, "Expression passed to %s(...) is not a " "script-defined function.", kw) /* Note that this throws an exception, but __using(<EMPTY>) in the wrong place is a syntax error. Feature or bug? */; }else{ imports = fst->vImported; cwal_value_ref(imports); } cwal_value_unref(xrv); xrv = 0; } if(imports){ assert(!rc); cwal_value_rescope(se->scopes.current->cwalScope, imports) /* i don't think this is strictly necessary, as the input function cannot possibly live in a too-old scope, but better safe than sorry. */; cwal_value_unhand(imports); *rv = imports; }else if(!rc){ *rv = cwal_value_undefined(); } return rc; } #undef MARKER |
Changes to s2/s2_func.c.
︙ | ︙ | |||
61 62 63 64 65 66 67 68 69 70 71 72 73 74 | assert(se && theFunc && fst); props = fst->vImported; if(!props){ props = cwal_new_object_value(se->e); if(!props) return 0; s2_value_to_lhs_scope(theFunc, props); cwal_value_ref(props); fst->vImported = props; } return props; } /** Implements Function.importSymbols(). | > | 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 | assert(se && theFunc && fst); props = fst->vImported; if(!props){ props = cwal_new_object_value(se->e); if(!props) return 0; s2_value_to_lhs_scope(theFunc, props); cwal_value_ref(props); cwal_value_prototype_set(props, NULL); fst->vImported = props; } return props; } /** Implements Function.importSymbols(). |
︙ | ︙ |
Changes to s2/s2_internal.h.
︙ | ︙ | |||
19 20 21 22 23 24 25 26 27 28 29 30 31 32 | /** @internal S2_UNUSED_VAR exists only to squelch, in non-debug builds, warnings about the existence of vars which are only used in assert() expressions (and thus get filtered out in non-debug builds). */ #define S2_UNUSED_VAR __attribute__((__unused__)) typedef struct s2_op s2_op; /** @internal Internal state for Object.eachProperty() and similar functions. */ | > > > > > > > > > > > > > | 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | /** @internal S2_UNUSED_VAR exists only to squelch, in non-debug builds, warnings about the existence of vars which are only used in assert() expressions (and thus get filtered out in non-debug builds). */ #define S2_UNUSED_VAR __attribute__((__unused__)) /** S2_UNUSED_ARG is used to squelch warnings about interface-level function parameters which are not used in a given implementation: @code int some_interface(type1 arg1, type2 arg2){ S2_UNUSED_ARG arg1; ... return 0; } @code */ #define S2_UNUSED_ARG (void) typedef struct s2_op s2_op; /** @internal Internal state for Object.eachProperty() and similar functions. */ |
︙ | ︙ | |||
433 434 435 436 437 438 439 | will be NULL. It gets rescoped along with the containing Function value. */ cwal_value * vSrc; /** An Object which holds all "imported" symbols associated with this | > | | > > > > > > | 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 | will be NULL. It gets rescoped along with the containing Function value. */ cwal_value * vSrc; /** An Object which holds all "imported" symbols associated with this function (via the "using" pseudo-keyword when defining a function or via Function.importSymbols(). When this function gets called, all of these symbols get imported into the call()'s scope. 20190911: it "might be interesting" to add a keyword, or even a local variable, to access this list from inside a function. Slightly tricky would be to only resolve that symbol from inside the body of the function it applies to (similar to how the break keyword only works from within a loop). 20191209: the __using() keyword now provides access to this. Part of that change is that this value's prototype is removed. We've never needed it in native code and remove it now so that script code does not inadvertently do anything silly with it or trip over inherited properties. */ cwal_value * vImported; /** The name of the function, if any, gets defined as a scope-local symbol when the function is called, just like this->vImported does. |
︙ | ︙ | |||
964 965 966 967 968 969 970 971 972 973 974 975 976 977 | int s2_handle_get_result( s2_engine * se, s2_ptoker const * pr, int rc ); /** @internal Fetches fst's "imported symbols" holder object, initializing it if needed (which includes rescoping it to theFunc's scope). theFunc _must_ be the Function Value to which fst is bound. Is highly intolerant of NULL arguments. Returns NULL on OOM, else the properties object (owned by fst resp. theFunc). */ cwal_value * s2_func_import_props( s2_engine * se, | > > > | 984 985 986 987 988 989 990 991 992 993 994 995 996 997 998 999 1000 | int s2_handle_get_result( s2_engine * se, s2_ptoker const * pr, int rc ); /** @internal Fetches fst's "imported symbols" holder object, initializing it if needed (which includes rescoping it to theFunc's scope). theFunc _must_ be the Function Value to which fst is bound. If this function creates the imported symbols holder, its prototype is removed. Is highly intolerant of NULL arguments. Returns NULL on OOM, else the properties object (owned by fst resp. theFunc). */ cwal_value * s2_func_import_props( s2_engine * se, |
︙ | ︙ |
Changes to s2/s2_ops.c.
︙ | ︙ | |||
1448 1449 1450 1451 1452 1453 1454 | Maintenance reminder: s2_op_f_assign2() and s2_op_f_assign3() share most of their logic, but having them separate improves their readability considerably. It might be useful to separate parts of the two implementations (e.g. the main operator dispatch switch) into a shared function at some point. */ int s2_op_f_assign3( s2_op const * op, s2_engine * se, | | > < | 1448 1449 1450 1451 1452 1453 1454 1455 1456 1457 1458 1459 1460 1461 1462 1463 1464 1465 1466 1467 1468 1469 1470 1471 1472 1473 1474 1475 1476 1477 1478 1479 1480 1481 1482 1483 1484 1485 1486 | Maintenance reminder: s2_op_f_assign2() and s2_op_f_assign3() share most of their logic, but having them separate improves their readability considerably. It might be useful to separate parts of the two implementations (e.g. the main operator dispatch switch) into a shared function at some point. */ int s2_op_f_assign3( s2_op const * op, s2_engine * se, int argc, cwal_value **rv ){ int rc = 0; cwal_value * rhs, * lhs, * self /* Assignment op: self[lhs]=rhs */; s2_stoken * vTok; int opReturnedLhs = -1 /* optimization. If an operator returns the (X.Y) part of (X.Y OP Z) then we can skip the assignment back to X.Y. A value of <0 means we don't yet know. */; S2_UNUSED_ARG argc; assert( 3==op->arity ); rhs = s2__pop_val(se); vTok = s2__pop_tok(se, 1); lhs = vTok->value; self = s2__pop_val(se); assert(self); cwal_value_ref(lhs); cwal_value_ref(rhs); cwal_value_ref(self); s2_stoken_free(se, vTok, 1); vTok = 0; if(se->skipLevel>0){ cwal_value_unref(lhs); cwal_value_unref(rhs); cwal_value_unref(self); *rv = cwal_value_undefined(); return 0; } if(op->id != S2_T_OpAssign3 /* combo assignment, i.e. not: X.Y = Z */){ /* For combo ops (all but and X.Y=Z) we need to resolve the LHS first because it's needed for calculating the result. For plain assignments we don't need to do this, and we don't allow |
︙ | ︙ | |||
1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 | s2_stoken * vTok; int ttype; int opReturnedLhs = -1 /* optimization. If an operator returns the X part of (X OP Y) then we can skip the assignment back to X. A value of <0 means we don't yet know. */; char gotOverload = 0; rhs = s2__pop_val(se); vTok = s2__pop_tok(se, 1); lhs = vTok->value; assert( 2==op->arity ); cwal_value_ref(rhs); cwal_value_ref(lhs); ttype = vTok->ttype; s2_stoken_free(se, vTok, 1); vTok = 0; if(se->skipLevel>0){ *rv = cwal_value_undefined(); cwal_value_unref(rhs); cwal_value_unref(lhs); return 0; } if(S2_T_Identifier != ttype){ cwal_value_unref(rhs); cwal_value_unref(lhs); | > < | 1665 1666 1667 1668 1669 1670 1671 1672 1673 1674 1675 1676 1677 1678 1679 1680 1681 1682 1683 1684 1685 1686 1687 1688 1689 1690 1691 1692 1693 1694 1695 1696 1697 | s2_stoken * vTok; int ttype; int opReturnedLhs = -1 /* optimization. If an operator returns the X part of (X OP Y) then we can skip the assignment back to X. A value of <0 means we don't yet know. */; char gotOverload = 0; S2_UNUSED_ARG argc; rhs = s2__pop_val(se); vTok = s2__pop_tok(se, 1); lhs = vTok->value; assert( 2==op->arity ); cwal_value_ref(rhs); cwal_value_ref(lhs); ttype = vTok->ttype; s2_stoken_free(se, vTok, 1); vTok = 0; if(se->skipLevel>0){ *rv = cwal_value_undefined(); cwal_value_unref(rhs); cwal_value_unref(lhs); return 0; } if(S2_T_Identifier != ttype){ cwal_value_unref(rhs); cwal_value_unref(lhs); return s2_engine_err_set(se, CWAL_SCR_SYNTAX, "Invalid LHS (%s) for '%s' op.", s2_ttype_cstr(ttype), op->sym); } if(op->id != S2_T_OpAssign /* a combo assignment, not plain X=Y */){ /* For combo ops (all but X=Y) we need to resolve the LHS first |
︙ | ︙ |
Changes to s2/s2_t10n.c.
︙ | ︙ | |||
1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 | CASE(S2_T_KeywordThrow); CASE(S2_T_KeywordTrue); CASE(S2_T_KeywordTry); CASE(S2_T_KeywordTypeinfo); CASE(S2_T_KeywordTypename); CASE(S2_T_KeywordUndefined); CASE(S2_T_KeywordUnset); CASE(S2_T_KeywordVar); CASE(S2_T_KeywordWhile); CASE(S2_T_Comment__); CASE(S2_T_CommentC); CASE(S2_T_CommentCpp); CASE(S2_T_Mark__); | > | 1857 1858 1859 1860 1861 1862 1863 1864 1865 1866 1867 1868 1869 1870 1871 | CASE(S2_T_KeywordThrow); CASE(S2_T_KeywordTrue); CASE(S2_T_KeywordTry); CASE(S2_T_KeywordTypeinfo); CASE(S2_T_KeywordTypename); CASE(S2_T_KeywordUndefined); CASE(S2_T_KeywordUnset); CASE(S2_T_Keyword__Using); CASE(S2_T_KeywordVar); CASE(S2_T_KeywordWhile); CASE(S2_T_Comment__); CASE(S2_T_CommentC); CASE(S2_T_CommentCpp); CASE(S2_T_Mark__); |
︙ | ︙ |
Changes to s2/s2_t10n.h.
︙ | ︙ | |||
247 248 249 250 251 252 253 254 255 256 257 258 259 260 | S2_T_KeywordThrow, S2_T_KeywordTrue, S2_T_KeywordTry, S2_T_KeywordTypeinfo, S2_T_KeywordTypename, S2_T_KeywordUndefined, S2_T_KeywordUnset, S2_T_KeywordVar, S2_T_KeywordWhile, S2_T_Comment__ = 4000, S2_T_CommentC, S2_T_CommentCpp, | > | 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 | S2_T_KeywordThrow, S2_T_KeywordTrue, S2_T_KeywordTry, S2_T_KeywordTypeinfo, S2_T_KeywordTypename, S2_T_KeywordUndefined, S2_T_KeywordUnset, S2_T_Keyword__Using /* __using(), as opposed to function()using(...){} */, S2_T_KeywordVar, S2_T_KeywordWhile, S2_T_Comment__ = 4000, S2_T_CommentC, S2_T_CommentCpp, |
︙ | ︙ |
Changes to s2/unit/300-000-functions.s2.
︙ | ︙ | |||
200 201 202 203 204 205 206 | var f = proc(){return o} using{o:{x:1}}; assert 1 === f().x; assert 1 === f().x++; assert 3 === ++f().x; assert 2 === --(f()).x; //assert 3 === ++(f().x) /* doesn't work b/c (x) is resolved in a subexpression */; } | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 | var f = proc(){return o} using{o:{x:1}}; assert 1 === f().x; assert 1 === f().x++; assert 3 === ++f().x; assert 2 === --(f()).x; //assert 3 === ++(f().x) /* doesn't work b/c (x) is resolved in a subexpression */; } var u = scope { /* 20191209: __using() keyword */ assert undefined === proc(){return __using()}(); var f = proc(){return __using()} using{a: 1}; var u = f(); assert typeinfo(isobject u); assert !u.prototype; assert 1 === u.a; u.b = 2; assert 2 === f().b; assert undefined === __using(proc(){}); assert 'CWAL_SCR_SYNTAX' === catch {__using()}.codeString(); assert 'CWAL_RC_TYPE' === catch __using(f.apply).codeString(); f = proc(){return __using()} using{} /* as fate would have it, using{} is legal but using() is not. */; u = f(); assert typeinfo(isobject u); u.b = 1; assert 1 === f().b; /* imports must survive propagation out of a temporary value... */ u = __using(proc()using{a:999}{}); assert typeinfo(isobject u); assert 999 === u.a; /* imports can be added after the fact with importSymbols()... */ f = proc(){return __using()}; assert undefined === f(); f.importSymbols({a: 1000}); assert 1000 === f().a; /* imports must survive propagation out of this scope... */ f() /* scope result */; }; assert typeinfo(isobject u); assert 1000 === u.a; |