cwal

Check-in [a009b47e67]
Login

Check-in [a009b47e67]

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: a009b47e67e1e5dbc78a7b4dcad977250caaa096
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
Unified Diff Ignore Whitespace Patch
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
406
407
408
409
410
411
412

413
414
415
416
417
418
419
  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_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 },







|






>







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
954
955
956
957
958
959
960
961
962
963

   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







|
|
|







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
2128
2129
2130
2131
2132
2133
2134
2135
                         : "<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"); */

  /* See if it's an empty script-side function. */
  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;







<







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
2157


2158
2159
2160
2161
2162
2163
2164
    if(rc){
      cwal_value_make_vacuum_proof(arV, 0);
      cwal_value_unref(arV);
      goto end;
    }
    oldArgV = se->callArgV;
    se->callArgV = 0;
    /* se->currentScriptFunc = 0; */


    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");
    }








|
>
>







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
2202
2203
2204
2205
2206
2207
2208
2209
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
                                      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 a lower 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: seems 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()







|









|
|







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
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
      /* 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:
  /* s2_dotop_state( se, 0, 0, 0 ); */
  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. 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));*/







<




















|
|
>
|







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
2366
2367
2368
2369
2370
2371
2372
2373
2374
2375
2376

   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 :`(. The
   solution is as yet unknown :`(.

   Other formulations and permuations:

   (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








|
|

|







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
2651
2652
2653
2654
2655
2656
2657
2658
#  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->currentScope->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);







|







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
4267


4268
4269
4270
4271
4272
4273
4274
  }
  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;
  if(kw || se || pr){/*avoid unused param warning*/}


  *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







|
>
>







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
4296
4297
4298
4299
4300
4301
4302
4303
    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");
        if(kw){/*avoid unused param warning*/}
        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;
    }
  }
}








|







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
6774
6775
6776
6777
6778
6779
6780
6781
#endif
    if(fs){
      /* Import imported properties (using/Function.importSymbols())
         into the current scope.
      */
      if(fs->vImported){
        rc = cwal_scope_import_properties( s, fs->vImported );
        if(stateUnused){/*avoid unused param warning*/}
      }
    }
    return rc;
  }
}

int s2_callback_hook_post(cwal_callback_args const * args,







<







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
7919
7920
7921
7922
7923
7924
7925
7926
  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_ scope was started by 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()).







|







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

440
441
442
443
444
445
446
447






448
449
450
451
452
453
454
     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. 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).






  */
  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.







>
|
|






>
>
>
>
>
>







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
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. */;

  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();
    if(argc){/*avoid unused param warning*/}
    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







|







>
















<







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
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;

  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);
    if(argc){/*avoid unused param warning*/}
    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







>


















<







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;