Login
Check-in [6085b8689d]
Login

Many hyperlinks are disabled.
Use anonymous login to enable hyperlinks.

Overview
Comment:latest s2, re-enabled test combinations disabled when string interning was recently turned off.
Downloads: Tarball | ZIP archive
Timelines: family | ancestors | descendants | both | trunk
Files: files | file ages | folders
SHA1: 6085b8689d1a3850dce4c7457fe2d6971f0fa08f
User & Date: stephan 2016-01-31 17:54:04.675
Context
2016-02-10
16:30
Updated s2 and related build bits, removed the libfossil-injected s2.Buffer.compress() and friends because those have been ported over to the s2 core. check-in: a25251e6b0 user: stephan tags: trunk
2016-01-31
17:54
latest s2, re-enabled test combinations disabled when string interning was recently turned off. check-in: 6085b8689d user: stephan tags: trunk
2016-01-28
12:40
minor script updates. check-in: 1cffc35efe user: stephan tags: trunk
Changes
Unified Diff Ignore Whitespace Patch
Changes to s2/Makefile.
162
163
164
165
166
167
168


169
170


171
172
173
174
175
176
unit: unit-proxy
unit2: UNIT_SCRIPTS_ALL:=$(filter unit2/%,$(UNIT_SCRIPT_LIST))
unit2: unit-proxy
.PHONY: unit-r
.PHONY: unit-rc
unit-r: S2SH.SHELL.FLAGS:=--a --R --C
unit-r: unit


unit-rc: S2SH.SHELL.FLAGS:=--a --R --C
unit-rc: unit


units:
	@for i in unit unit-r unit-rc; do \
		echo "Making $$i ..."; \
		$(MAKE) $$i || exit $$?; \
	done
include vg.make







>
>


>
>

|




162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
unit: unit-proxy
unit2: UNIT_SCRIPTS_ALL:=$(filter unit2/%,$(UNIT_SCRIPT_LIST))
unit2: unit-proxy
.PHONY: unit-r
.PHONY: unit-rc
unit-r: S2SH.SHELL.FLAGS:=--a --R --C
unit-r: unit
unit-s: S2SH.SHELL.FLAGS:=--a -R -C --S
unit-s: unit
unit-rc: S2SH.SHELL.FLAGS:=--a --R --C
unit-rc: unit
unit-rsc: S2SH.SHELL.FLAGS:=--a --R --C --S
unit-rsc: unit
units:
	@for i in unit unit-r unit-rc unit-s unit-rsc; do \
		echo "Making $$i ..."; \
		$(MAKE) $$i || exit $$?; \
	done
include vg.make
Changes to s2/r-tester.sh.
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
    echo "Massified $test: try: ms_print ${msp} | less"
}

if [ -e f-s2sh ]; then
    # kludge for the libfossil source tree
    S2SH_ADD_FLAGS="${S2SH_ADD_FLAGS} -a"
fi
S2SHFLAGS="--a -R ${S2SH_ADD_FLAGS}"
# Reminder: some fsl modules rely on code set up by s2sh init script,
# so we cannot use the --a option in libfossil.
echo S2SHFLAGS=$S2SHFLAGS
for test in $list; do
    echo "Running require.s2 test: ${test}"
    outfile=${rdir}/${test}.test_out
    verbose "Output going to: $outfile"







|







136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
    echo "Massified $test: try: ms_print ${msp} | less"
}

if [ -e f-s2sh ]; then
    # kludge for the libfossil source tree
    S2SH_ADD_FLAGS="${S2SH_ADD_FLAGS} -a"
fi
S2SHFLAGS="--a -R -S ${S2SH_ADD_FLAGS}"
# Reminder: some fsl modules rely on code set up by s2sh init script,
# so we cannot use the --a option in libfossil.
echo S2SHFLAGS=$S2SHFLAGS
for test in $list; do
    echo "Running require.s2 test: ${test}"
    outfile=${rdir}/${test}.test_out
    verbose "Output going to: $outfile"
Changes to s2/s2_amalgamation.c.
14281
14282
14283
14284
14285
14286
14287


14288
14289
14290
14291
14292
14293
14294
CWAL_FMT_DOUBLE,
CWAL_FMT_STRING,
CWAL_FMT_STRING_SQL,
CWAL_FMT_CHAR,
CWAL_FMT_TYPENAME,
CWAL_FMT_BLOB,
CWAL_FMT_JSON,


CWAL_FMT_MAX
};
typedef enum cwal_format_t cwal_format_t;

enum cwal_format_flags {
CWAL_FMT_F_NONE = 0,
CWAL_FMT_F_SIGNED = 0x01,







>
>







14281
14282
14283
14284
14285
14286
14287
14288
14289
14290
14291
14292
14293
14294
14295
14296
CWAL_FMT_DOUBLE,
CWAL_FMT_STRING,
CWAL_FMT_STRING_SQL,
CWAL_FMT_CHAR,
CWAL_FMT_TYPENAME,
CWAL_FMT_BLOB,
CWAL_FMT_JSON,
CWAL_FMT_URLENCODE,
CWAL_FMT_URLDECODE,
CWAL_FMT_MAX
};
typedef enum cwal_format_t cwal_format_t;

enum cwal_format_flags {
CWAL_FMT_F_NONE = 0,
CWAL_FMT_F_SIGNED = 0x01,
14361
14362
14363
14364
14365
14366
14367




14368
14369
14370
14371
14372
14373
14374
                                  cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_basics( cwal_engine * e, cwal_buffer * tgt,
                                    cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_blob( cwal_engine * e, cwal_buffer * tgt,
                                  cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_json( cwal_engine * e, cwal_buffer * tgt,
                                  cwal_format_info * fi, cwal_value const * v );





static const cwal_format_info CWAL_FORMAT_INFO[CWAL_FMT_MAX+1] = {
/*
  Maintenance reminder: these MUST be in the same order the entries
  are defined in cwal_format_t.
*/
/* {type,flags,precision,width,position,errMsg,pad,f} */







>
>
>
>







14363
14364
14365
14366
14367
14368
14369
14370
14371
14372
14373
14374
14375
14376
14377
14378
14379
14380
                                  cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_basics( cwal_engine * e, cwal_buffer * tgt,
                                    cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_blob( cwal_engine * e, cwal_buffer * tgt,
                                  cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_json( cwal_engine * e, cwal_buffer * tgt,
                                  cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_urlenc( cwal_engine * e, cwal_buffer * tgt,
                                    cwal_format_info * fi, cwal_value const * v );
static int cwal_format_part_urldec( cwal_engine * e, cwal_buffer * tgt,
                                    cwal_format_info * fi, cwal_value const * v );

static const cwal_format_info CWAL_FORMAT_INFO[CWAL_FMT_MAX+1] = {
/*
  Maintenance reminder: these MUST be in the same order the entries
  are defined in cwal_format_t.
*/
/* {type,flags,precision,width,position,errMsg,pad,f} */
14385
14386
14387
14388
14389
14390
14391


14392
14393
14394
14395
14396
14397
14398
FMT(DOUBLE,double),
FMT(STRING,string),
FMT(STRING_SQL,sql),
FMT(CHAR,char),
FMT(TYPENAME,basics),
FMT(BLOB,blob),
FMT(JSON,json),


#undef FMT
{CWAL_FMT_MAX, 0, 0, 0, 0,NULL,' ', NULL},
};

static const cwal_format_info cwal_format_info_empty = {
CWAL_FMT_UNKNOWN,
0/*flags*/,







>
>







14391
14392
14393
14394
14395
14396
14397
14398
14399
14400
14401
14402
14403
14404
14405
14406
FMT(DOUBLE,double),
FMT(STRING,string),
FMT(STRING_SQL,sql),
FMT(CHAR,char),
FMT(TYPENAME,basics),
FMT(BLOB,blob),
FMT(JSON,json),
FMT(URLENCODE,urlenc),
FMT(URLDECODE,urldec),
#undef FMT
{CWAL_FMT_MAX, 0, 0, 0, 0,NULL,' ', NULL},
};

static const cwal_format_info cwal_format_info_empty = {
CWAL_FMT_UNKNOWN,
0/*flags*/,
14750
14751
14752
14753
14754
14755
14756







































































































































14757
14758
14759
14760
14761
14762
14763
            for( ; !rc && (width > n); --width){
                rc = cwal_buffer_append( e, tgt, &fi->pad, 1 );
            }
        }
        return rc;
    }
}







































































































































/**
   Parse the range [b,*e) as a java.lang.String.format()-style
   format string:

   %N$[flags][width][.precision][type]
*/
static int cwal_format_parse_info( char const * b, char const ** e,







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







14758
14759
14760
14761
14762
14763
14764
14765
14766
14767
14768
14769
14770
14771
14772
14773
14774
14775
14776
14777
14778
14779
14780
14781
14782
14783
14784
14785
14786
14787
14788
14789
14790
14791
14792
14793
14794
14795
14796
14797
14798
14799
14800
14801
14802
14803
14804
14805
14806
14807
14808
14809
14810
14811
14812
14813
14814
14815
14816
14817
14818
14819
14820
14821
14822
14823
14824
14825
14826
14827
14828
14829
14830
14831
14832
14833
14834
14835
14836
14837
14838
14839
14840
14841
14842
14843
14844
14845
14846
14847
14848
14849
14850
14851
14852
14853
14854
14855
14856
14857
14858
14859
14860
14861
14862
14863
14864
14865
14866
14867
14868
14869
14870
14871
14872
14873
14874
14875
14876
14877
14878
14879
14880
14881
14882
14883
14884
14885
14886
14887
14888
14889
14890
14891
14892
14893
14894
14895
14896
14897
14898
14899
14900
14901
14902
14903
14904
14905
14906
            for( ; !rc && (width > n); --width){
                rc = cwal_buffer_append( e, tgt, &fi->pad, 1 );
            }
        }
        return rc;
    }
}

static int cwal_httpurl_needs_escape( int c )
{
  /*
    Definition of "safe" and "unsafe" chars
    was taken from:

    http://www.codeguru.com/cpp/cpp/cpp_mfc/article.php/c4029/
  */
  return ( (c >= 32 && c <=47)
           || ( c>=58 && c<=64)
           || ( c>=91 && c<=96)
           || ( c>=123 && c<=126)
           || ( c<32 || c>=127)
           );
}

int cwal_format_part_urlenc( cwal_engine * e,
                             cwal_buffer * tgt,
                             cwal_format_info * fi,
                             cwal_value const * v ){
    cwal_size_t slen = 0;
    char const * str = cwal_value_get_cstr(v, &slen);
    char ch;
    int rc = 0;
    cwal_size_t j = 0;
    char * bufpt = NULL;
    cwal_size_t const oldUsed = e->buffer.used;
    static char const * hex = "0123456789ABCDEF";
    if(!str){
        fi->errMsg = "URL encoding requires a String value.";
        return CWAL_RC_TYPE;
    }
    rc = cwal_buffer_reserve(e, &e->buffer, oldUsed + slen * 3 );
    if(rc) return rc;
    bufpt = (char *)(e->buffer.mem + oldUsed);
    ch = *str;
    for( ; !rc && ch; ch = *(++str) ){
        if(!cwal_httpurl_needs_escape(ch) ){
            bufpt[j++] = ch;
        }else{
            bufpt[j++] = '%';
            bufpt[j++] = hex[((ch>>4)&0xf)];
            bufpt[j++] = hex[(ch&0xf)];
        }
    }
    assert( (void *)bufpt == (void*)e->buffer.mem );
    rc = cwal_buffer_append(e, tgt, bufpt, j);
    e->buffer.used = oldUsed;
    e->buffer.mem[e->buffer.used] = 0;
    return 0;
}

/* 
   cwal_hexchar_to_int():

   For 'a'-'f', 'A'-'F' and '0'-'9', returns the appropriate decimal
   number.  For any other character it returns -1.
*/
static int cwal_hexchar_to_int( int ch )
{
  if( (ch>='0' && ch<='9') ) return ch-'0';
  else if( (ch>='a' && ch<='f') ) return ch-'a'+10;
  else if( (ch>='A' && ch<='F') ) return ch-'A'+10;
  else return -1;
}

static int cwal_is_xdigit( int ch ){
  return ('a'<=ch && 'f'>=ch)
    ? 1
    : (('A'<=ch && 'F'>=ch)
       ? 1
       : ('0'<=ch && '9'>=ch ? 1 : 0));
}

int cwal_format_part_urldec( cwal_engine * e,
                             cwal_buffer * tgt,
                             cwal_format_info * fi,
                             cwal_value const * v ){

    cwal_size_t slen = 0;
    char const * str = cwal_value_get_cstr(v, &slen);
    int rc = 0;
    char ch = 0, ch2 = 0;
    char xbuf[4];
    int decoded;
    if(!str){
        fi->errMsg = "URL decoding requires a String value.";
        return CWAL_RC_TYPE;
    }
    else if(!slen) return 0;
    else if(slen<3){
        /* can't be urlencoded */
        return cwal_buffer_append(e, tgt, str, slen);
    }
    ch = *str;
    while( !rc && ch ){
        if( ch == '%' ){
            ch = *(++str);
            ch2 = *(++str);
            if( cwal_is_xdigit((int)ch) &&
                cwal_is_xdigit((int)ch2) ){
                decoded = (cwal_hexchar_to_int( ch ) * 16)
                    + cwal_hexchar_to_int( ch2 );
                xbuf[0] = (char)decoded;
                xbuf[1] = 0;
                rc = cwal_buffer_append(e, tgt, xbuf, 1);
                ch = *(++str);
                continue;
            }
            else{
                xbuf[0] = '%';
                xbuf[1] = ch;
                xbuf[2] = ch2;
                xbuf[3] = 0;
                rc = cwal_buffer_append(e, tgt, xbuf, 3);
                ch = *(++str);
                continue;
            }
        }
        else if( ch == '+' ){
            xbuf[0] = ' ';
            xbuf[1] = 0;
            rc = cwal_buffer_append(e, tgt, xbuf, 1);
            ch = *(++str);
            continue;
        }
        xbuf[0] = ch;
        xbuf[1] = 0;
        rc = cwal_buffer_append(e, tgt, xbuf, 1);
        ch = *(++str);
    }
    return rc;
}

/**
   Parse the range [b,*e) as a java.lang.String.format()-style
   format string:

   %N$[flags][width][.precision][type]
*/
static int cwal_format_parse_info( char const * b, char const ** e,
14893
14894
14895
14896
14897
14898
14899






14900
14901
14902
14903
14904







14905
14906
14907
14908
14909
14910
14911

14912
14913
14914
14915
14916

14917
14918
14919
14920
14921
14922
14923
          break;
      case 'x': fi->type = CWAL_FMT_INT_HEXLC;
          ++p;
          break;
      case 'X': fi->type = CWAL_FMT_INT_HEXUC;
          ++p;
          break;






      default:
          fi->errMsg = "Unknown format type specifier.";
          return CWAL_RC_RANGE;
    }
    switch(fi->type){







      case CWAL_FMT_INT_DEC:
      case CWAL_FMT_INT_HEXLC:
      case CWAL_FMT_INT_HEXUC:
          if(fi->precision && cwalFormatNoPrecision != fi->precision){
              fi->errMsg = "Integer conversion does not support precision.";
              return CWAL_RC_MISUSE;
          }

      case CWAL_FMT_CHAR:
          if(fi->precision<0){
              fi->errMsg = "Character precision (repetition) may not be negative.";
              return CWAL_RC_MISUSE;
          }

      default:
          break;
    }
    *e = p;
    fi->f = CWAL_FORMAT_INFO[fi->type].f;
    return 0;
}







>
>
>
>
>
>





>
>
>
>
>
>
>




|


>





>







15036
15037
15038
15039
15040
15041
15042
15043
15044
15045
15046
15047
15048
15049
15050
15051
15052
15053
15054
15055
15056
15057
15058
15059
15060
15061
15062
15063
15064
15065
15066
15067
15068
15069
15070
15071
15072
15073
15074
15075
15076
15077
15078
15079
15080
15081
          break;
      case 'x': fi->type = CWAL_FMT_INT_HEXLC;
          ++p;
          break;
      case 'X': fi->type = CWAL_FMT_INT_HEXUC;
          ++p;
          break;
      case 'r': fi->type = CWAL_FMT_URLENCODE;
          ++p;
          break;
      case 'R': fi->type = CWAL_FMT_URLDECODE;
          ++p;
          break;
      default:
          fi->errMsg = "Unknown format type specifier.";
          return CWAL_RC_RANGE;
    }
    switch(fi->type){
      case CWAL_FMT_URLENCODE:
      case CWAL_FMT_URLDECODE:
          if(fi->width){
              fi->errMsg = "Conversion does not support width.";
              return CWAL_RC_MISUSE;
          }
          /* fall through */
      case CWAL_FMT_INT_DEC:
      case CWAL_FMT_INT_HEXLC:
      case CWAL_FMT_INT_HEXUC:
          if(fi->precision && cwalFormatNoPrecision != fi->precision){
              fi->errMsg = "Conversion does not support precision.";
              return CWAL_RC_MISUSE;
          }
          break;
      case CWAL_FMT_CHAR:
          if(fi->precision<0){
              fi->errMsg = "Character precision (repetition) may not be negative.";
              return CWAL_RC_MISUSE;
          }
          break;
      default:
          break;
    }
    *e = p;
    fi->f = CWAL_FORMAT_INFO[fi->type].f;
    return 0;
}
21758
21759
21760
21761
21762
21763
21764
21765
21766
21767
21768
21769
21770
21771
21772
21773
21774
21775
21776
21777
21778
21779
21780
21781
21782
21783
21784
21785
21786



21787
21788
21789
21790
21791
21792
21793
    assert((st->size >= op->arity)
           || (op->assoc>0 && op->arity==1 /* unary +, -, ~ */));
#endif
    if(st->size < op->arity){
      rc = s2_engine_err_set(se, CWAL_RC_RANGE,
                             "Not enough operands on the stack.");
    }else{
#if 0
      /* In principal we could move the skip-level checking out of the
         individual ops and into here, but we need more info: some ops
         set &rv, some don't, and we don't have that info here without
         calling the op, and that info changes how we handle the end
         of the process.
      */
      if(se->skipLevel>0){
        int i = 0;
        for( ; i < op->arity; ++i ){
          s2_engine_pop_token(se, 1);
        }
        MARKER(("Popped %d arg(s) for %d-ary op '%s'\n",i, op->arity, op->sym ));
      }else{
        rc = op->call(op, se, op->arity, &rv);
      }
#else
      rc = op->call(op, se, op->arity, &rv);
#endif
      popArgCount = op->arity;
    }
  }else{



    /* Variadic operator ... */
    s2_stoken * t = se->st.vals.top;
    int i = 0;
    char doBreak = 0;
    /* Count how the arguments by looking for
       a S2_T_MarkVariadicStart token. */
    for( ; t && !doBreak && (i <st->size); t = t->next ){







<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<



>
>
>







21916
21917
21918
21919
21920
21921
21922














21923




21924
21925
21926
21927
21928
21929
21930
21931
21932
21933
21934
21935
21936
    assert((st->size >= op->arity)
           || (op->assoc>0 && op->arity==1 /* unary +, -, ~ */));
#endif
    if(st->size < op->arity){
      rc = s2_engine_err_set(se, CWAL_RC_RANGE,
                             "Not enough operands on the stack.");
    }else{














      rc = op->call(op, se, op->arity, &rv);




      popArgCount = op->arity;
    }
  }else{
#if 1
    assert(!"not possible");
#else
    /* Variadic operator ... */
    s2_stoken * t = se->st.vals.top;
    int i = 0;
    char doBreak = 0;
    /* Count how the arguments by looking for
       a S2_T_MarkVariadicStart token. */
    for( ; t && !doBreak && (i <st->size); t = t->next ){
21812
21813
21814
21815
21816
21817
21818

21819
21820
21821
21822
21823
21824
21825
        s2_stoken * variadicCheck = s2_engine_peek_token(se);
        assert(variadicCheck);
        assert(S2_T_MarkVariadicStart == variadicCheck->ttype);
      }
      s2_engine_pop_token( se, 0 ) /* S2_T_MarkVariadicStart */;
      popArgCount = i + 1 /* variadic marker */;
    }

  }
  if(!rc){
    assert( st->size == (oldStackSize - popArgCount) );
    if(st->size != (oldStackSize - popArgCount)){
      rc = s2_engine_err_set(se, CWAL_RC_MISUSE,
                             "Unexpected stack size after "
                             "running operator '%s'\n",







>







21955
21956
21957
21958
21959
21960
21961
21962
21963
21964
21965
21966
21967
21968
21969
        s2_stoken * variadicCheck = s2_engine_peek_token(se);
        assert(variadicCheck);
        assert(S2_T_MarkVariadicStart == variadicCheck->ttype);
      }
      s2_engine_pop_token( se, 0 ) /* S2_T_MarkVariadicStart */;
      popArgCount = i + 1 /* variadic marker */;
    }
#endif
  }
  if(!rc){
    assert( st->size == (oldStackSize - popArgCount) );
    if(st->size != (oldStackSize - popArgCount)){
      rc = s2_engine_err_set(se, CWAL_RC_MISUSE,
                             "Unexpected stack size after "
                             "running operator '%s'\n",
23885
23886
23887
23888
23889
23890
23891





23892
23893
23894
23895
23896
23897
23898

23899
23900
23901
23902
23903
23904
23905
23906
23907
23908
23909
23910
23911
23912
23913
23914
23915

23916
23917
23918
23919
23920
23921
23922
23923
23924
23925



23926
23927
23928
23929
23930
23931
23932
  return se ? se->e : 0;
}

s2_scope * s2_scope_current( s2_engine * se ){
  return (se && se->e) ? se->currentScope : 0;
}






int s2_scope_push( s2_engine * se, s2_scope * tgt ){
  int rc;
  cwal_scope * pScope = &tgt->scope;
  assert(se);
  assert(se->e);
  assert(tgt);
  assert(!tgt->parent);

  rc = cwal_scope_push( se->e, &pScope );
  if(!rc){
    /* assert(tgt->scope.parent); */
    if((int)tgt->scope.level > se->metrics.maxScopeDepth){
      se->metrics.maxScopeDepth = (int)tgt->scope.level;
    }
    tgt->parent = se->currentScope;
    se->currentScope = tgt;
    se->sguard = &tgt->sguard;
    /* MARKER(("pushed %p\n",(void const *)tgt));  */

    /* Make sure that ternary depth is not counted across scopes. We
       need a similar hack for non-scope-pushing blocks, now that i
       think about it. */
    s2_engine_subexpr_save(se, &tgt->saved);
  }else{
    assert(!"This can't happen if the args are valid!");

  }
  return rc;
}

int s2_scope_pop2( s2_engine * se, cwal_value * upScopeThis ){
  int rc;
  s2_scope * sc = se->currentScope;
  assert(se);
  assert(se->e);
  assert(se->e->current);



  if(sc==&se->topScope){
    upScopeThis = 0 /* cannot propagate this */;
    cwal_exception_set(se->e, 0);
    s2_propagating_set(se, 0);
  }
  assert(se->e->current == &sc->scope);
  if(upScopeThis){







>
>
>
>
>

|





>

















>










>
>
>







24029
24030
24031
24032
24033
24034
24035
24036
24037
24038
24039
24040
24041
24042
24043
24044
24045
24046
24047
24048
24049
24050
24051
24052
24053
24054
24055
24056
24057
24058
24059
24060
24061
24062
24063
24064
24065
24066
24067
24068
24069
24070
24071
24072
24073
24074
24075
24076
24077
24078
24079
24080
24081
24082
24083
24084
24085
24086
  return se ? se->e : 0;
}

s2_scope * s2_scope_current( s2_engine * se ){
  return (se && se->e) ? se->currentScope : 0;
}


/* defined in s2_eval.c */
void s2_dotop_state( s2_engine * se, cwal_value * self,
                     cwal_value * lhs, cwal_value * key );

int s2_scope_push( s2_engine * se, s2_scope * tgt ){
  int rc = 0;
  cwal_scope * pScope = &tgt->scope;
  assert(se);
  assert(se->e);
  assert(tgt);
  assert(!tgt->parent);
  /* s2_dotop_state(se, 0, 0, 0); */
  rc = cwal_scope_push( se->e, &pScope );
  if(!rc){
    /* assert(tgt->scope.parent); */
    if((int)tgt->scope.level > se->metrics.maxScopeDepth){
      se->metrics.maxScopeDepth = (int)tgt->scope.level;
    }
    tgt->parent = se->currentScope;
    se->currentScope = tgt;
    se->sguard = &tgt->sguard;
    /* MARKER(("pushed %p\n",(void const *)tgt));  */

    /* Make sure that ternary depth is not counted across scopes. We
       need a similar hack for non-scope-pushing blocks, now that i
       think about it. */
    s2_engine_subexpr_save(se, &tgt->saved);
  }else{
    assert(!"This can't happen if the args are valid!");
    rc = CWAL_RC_ASSERT;
  }
  return rc;
}

int s2_scope_pop2( s2_engine * se, cwal_value * upScopeThis ){
  int rc;
  s2_scope * sc = se->currentScope;
  assert(se);
  assert(se->e);
  assert(se->e->current);
  s2_dotop_state(se, 0, 0, 0)
    /* necessary to keep from holding unpropagated,
       stale refs */;
  if(sc==&se->topScope){
    upScopeThis = 0 /* cannot propagate this */;
    cwal_exception_set(se->e, 0);
    s2_propagating_set(se, 0);
  }
  assert(se->e->current == &sc->scope);
  if(upScopeThis){
23977
23978
23979
23980
23981
23982
23983

23984
23985
23986
23987
23988
23989
23990
       popping the scope don't like this at all.*/
    else{
      s2_engine_sweep(se);
    }
#endif
  }else{
    assert(!"This can't happen if the args are valid!");

  }
  return rc;
}

int s2_scope_pop( s2_engine * se ){
  return s2_scope_pop2(se, 0);
}







>







24131
24132
24133
24134
24135
24136
24137
24138
24139
24140
24141
24142
24143
24144
24145
       popping the scope don't like this at all.*/
    else{
      s2_engine_sweep(se);
    }
#endif
  }else{
    assert(!"This can't happen if the args are valid!");
    rc = CWAL_RC_ASSERT;
  }
  return rc;
}

int s2_scope_pop( s2_engine * se ){
  return s2_scope_pop2(se, 0);
}
24595
24596
24597
24598
24599
24600
24601
24602
24603
24604
24605
24606
24607
24608
24609
24610
24611
24612
24613
24614
24615
24616
24617
24618
24619
24620
24621
24622
24623
24624
24625
24626
24627
24628
24629
24630
24631
24632
24633


24634
24635
24636
24637
24638
24639
24640
24641
24642
24643
24644
  }else{
    *rv = destV;
    cwal_value_unhand(destV);
  }
  return rc;
}

int s2_set_ctor_method( s2_engine * se, cwal_value * container, cwal_function * method ){
  if(!se || !container || !method) return CWAL_RC_MISUSE;
  else if(!cwal_props_can(container)) return CWAL_RC_TYPE;
  else{
    return s2_set_with_flags_v( se, container, se->cache.keyCtorNew,
                                cwal_function_value(method),
                                CWAL_VAR_F_CONST | CWAL_VAR_F_HIDDEN );
  }
}

int s2_set_ctor_callback( s2_engine * se, cwal_value * container, cwal_callback_f method ){
  int rc;
  cwal_function * f;
  cwal_value * fv;
  if(!se || !container || !method) return CWAL_RC_MISUSE;
  else if(!cwal_props_can(container)) return CWAL_RC_TYPE;
  fv = s2_new_callback(se, method);
  f = fv ? cwal_value_get_function(fv) : NULL;
  if(f){
    cwal_value_ref(fv);
    rc = s2_set_ctor_method( se, container, f );
    cwal_value_unref(fv);
  }else{
    rc = CWAL_RC_OOM;
  }
  return rc;
    
}

char s2_value_is_newing( cwal_value const * thisForCurrentCall ){
  return 
    (S2_VAL_F_IS_NEWING &


     cwal_container_client_flags_get(thisForCurrentCall))
    ? 1 : 0;
}



int s2_ctor_fetch( s2_engine * se, s2_ptoker const * pr,
                   cwal_value * operand,
                   cwal_function **rv,
                   int errPolicy ){
  cwal_value * vtor = 0;







|









|









|








|
|
|
>
>
|


<







24750
24751
24752
24753
24754
24755
24756
24757
24758
24759
24760
24761
24762
24763
24764
24765
24766
24767
24768
24769
24770
24771
24772
24773
24774
24775
24776
24777
24778
24779
24780
24781
24782
24783
24784
24785
24786
24787
24788
24789
24790
24791
24792
24793

24794
24795
24796
24797
24798
24799
24800
  }else{
    *rv = destV;
    cwal_value_unhand(destV);
  }
  return rc;
}

int s2_ctor_method_set( s2_engine * se, cwal_value * container, cwal_function * method ){
  if(!se || !container || !method) return CWAL_RC_MISUSE;
  else if(!cwal_props_can(container)) return CWAL_RC_TYPE;
  else{
    return s2_set_with_flags_v( se, container, se->cache.keyCtorNew,
                                cwal_function_value(method),
                                CWAL_VAR_F_CONST | CWAL_VAR_F_HIDDEN );
  }
}

int s2_ctor_callback_set( s2_engine * se, cwal_value * container, cwal_callback_f method ){
  int rc;
  cwal_function * f;
  cwal_value * fv;
  if(!se || !container || !method) return CWAL_RC_MISUSE;
  else if(!cwal_props_can(container)) return CWAL_RC_TYPE;
  fv = s2_new_callback(se, method);
  f = fv ? cwal_value_get_function(fv) : NULL;
  if(f){
    cwal_value_ref(fv);
    rc = s2_ctor_method_set( se, container, f );
    cwal_value_unref(fv);
  }else{
    rc = CWAL_RC_OOM;
  }
  return rc;
    
}

char s2_scope_is_newing(s2_engine * se){
  return (&se->currentScope->scope
          == se->e->current
          /* ^^^ reminder: cwal_scope_push() may add scopes without s2
             seeing them. */
          && se->currentScope->newingThis)
    ? 1 : 0;
}



int s2_ctor_fetch( s2_engine * se, s2_ptoker const * pr,
                   cwal_value * operand,
                   cwal_function **rv,
                   int errPolicy ){
  cwal_value * vtor = 0;
25992
25993
25994
25995
25996
25997
25998



25999


26000
26001
26002
26003
26004
26005
26006
   key with (s2_keyword **) on s2_keyword::word.
*/
static int s2_keyword_cmp(void const * key, void const * kw){
  s2_keyword const * k = ((s2_keyword const *)key);
  s2_keyword const * w = *((s2_keyword const **)kw);
  if(k->wordLen == w->wordLen) return memcmp(k->word, w->word,
                                             (size_t)k->wordLen);



  else if(!w->word) return 1 /* sentinel entry */;


  else if(k->word[0] != w->word[0]) return (int)k->word[0] - (int)w->word[0];
  else{
    int const len = (k->wordLen<w->wordLen) ? k->wordLen : w->wordLen;
    int const cmp = memcmp(k->word, w->word, (size_t)len);
    return cmp ? cmp : ((len==k->wordLen) ? -1 : 1);
  }
}







>
>
>
|
>
>







26148
26149
26150
26151
26152
26153
26154
26155
26156
26157
26158
26159
26160
26161
26162
26163
26164
26165
26166
26167
   key with (s2_keyword **) on s2_keyword::word.
*/
static int s2_keyword_cmp(void const * key, void const * kw){
  s2_keyword const * k = ((s2_keyword const *)key);
  s2_keyword const * w = *((s2_keyword const **)kw);
  if(k->wordLen == w->wordLen) return memcmp(k->word, w->word,
                                             (size_t)k->wordLen);
#if !defined(NDEBUG)
  else if(!w->word){
    assert(!"sentinel gets trimmed from the list length");
    return 1 /* sentinel entry */;
  }
#endif
  else if(k->word[0] != w->word[0]) return (int)k->word[0] - (int)w->word[0];
  else{
    int const len = (k->wordLen<w->wordLen) ? k->wordLen : w->wordLen;
    int const cmp = memcmp(k->word, w->word, (size_t)len);
    return cmp ? cmp : ((len==k->wordLen) ? -1 : 1);
  }
}
26203
26204
26205
26206
26207
26208
26209



































































26210
26211
26212
26213
26214
26215
26216
      /* MARKER(("opErrPos=%p\n", (void const *)se->opErrPos)); */
      rc = s2_throw_err_ptoker(se, pr ? pr : se->currentScript);
      break;
  }
  return rc;

}




































































/**
   Internal helper for s2_eval_expr().

   Possibly processes pending operators in se's stack, depending on op
   and its precedence in relation to the operator (if any) to the
   left. Returns 0 on success (which includes it doing nothing of







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







26364
26365
26366
26367
26368
26369
26370
26371
26372
26373
26374
26375
26376
26377
26378
26379
26380
26381
26382
26383
26384
26385
26386
26387
26388
26389
26390
26391
26392
26393
26394
26395
26396
26397
26398
26399
26400
26401
26402
26403
26404
26405
26406
26407
26408
26409
26410
26411
26412
26413
26414
26415
26416
26417
26418
26419
26420
26421
26422
26423
26424
26425
26426
26427
26428
26429
26430
26431
26432
26433
26434
26435
26436
26437
26438
26439
26440
26441
26442
26443
26444
      /* MARKER(("opErrPos=%p\n", (void const *)se->opErrPos)); */
      rc = s2_throw_err_ptoker(se, pr ? pr : se->currentScript);
      break;
  }
  return rc;

}

/**
   Sets up se's internals so that it knows the most recent
   dot-op self/lhs/key. If self is not NULL then self is
   considered to be "this" in the corresponding downstream
   handling (i.e. it's a property access which binds "this"), else
   it does not. self is always lhs or 0.

   Pass all 0's to reset it.
*/
void s2_dotop_state( s2_engine * se, cwal_value * self,
                     cwal_value * lhs, cwal_value * key ){
    
#if 0
    se->dotOpSelf = self;
    se->dotOpLhs = lhs;
    se->dotOpKey = key;
#else
    /* Reminder the validity checks here point to, essentially, misuse
       in s2, where we've not cleaned up this state before it goes
       stale.

       A potential future problem is vacuuming-up of these pointers,
       which case we can't solve without a per-s2_scope
       array/container to hold these and make them vacuum-proof.
    */
    cwal_value * oldSelf = se->dotOpSelf;
    cwal_value * oldLhs = se->dotOpLhs;
    cwal_value * oldKey = se->dotOpKey;
    /**
       Assert that all of them appear to still be valid references
       (because it's easy to mess that up). These assertions are not
       guaranteed to trigger in all error cases, but they catch the
       most common one that we've prematurely unref'd a value and he
       have a pointer to its cwal-side recycling bin.
    */
    if(oldSelf){
        assert( cwal_value_refcount(oldSelf)
                || cwal_value_is_builtin(oldSelf) );
    }
    if(oldLhs){
        assert( cwal_value_refcount(oldLhs)
                || cwal_value_is_builtin(oldLhs));
    }
    if(oldKey){
        assert( cwal_value_refcount(oldKey)
                || cwal_value_is_builtin(oldKey) );
    }
    /**
       Because any of self/lhs/key can refer to or contain/own any
       other, as well as be the same instance of oldSelf/oldLhs/oldKey
       (in any combination!), we have to ref them all before we unref
       any of them.
    */
    if(self) cwal_value_ref(self);
    if(lhs) cwal_value_ref(lhs);
    if(key) cwal_value_ref(key);

    se->dotOpSelf = self;
    se->dotOpLhs = lhs;
    se->dotOpKey = key;

    if(oldSelf) cwal_value_unref(oldSelf);
    if(oldLhs) cwal_value_unref(oldLhs);
    if(oldKey) cwal_value_unref(oldKey);
#endif
}

/**
   Internal helper for s2_eval_expr().

   Possibly processes pending operators in se's stack, depending on op
   and its precedence in relation to the operator (if any) to the
   left. Returns 0 on success (which includes it doing nothing of
26304
26305
26306
26307
26308
26309
26310



































































26311
26312
26313
26314
26315
26316
26317
26318
26319
26320
26321
26322
26323
26324
26325
26326
26327
26328
26329
26330

26331
26332
26333
26334
26335
26336
26337
      /* se->currentScript = oldScript; */
      s2_engine_subexpr_restore(se, &save);
      if(sub.errPos) pr->errPos = sub.errPos;
      return s2_check_interrupted(se, rc);
    }
  }
}




































































int s2_eval_expr( s2_engine * se, s2_ptoker * st,
                  int evalFlags,
                  cwal_value ** rv){
  return s2_eval_expr_impl(se, st, 0, evalFlags, rv);
}

/**
   Expects pr to be a comma-separated list of expressions, until its
   EOF. Empty expressions and a stray trailing comma are not
   allowed. Evaluates each token and appends it to dest. Returns 0 on
   success. Some errors are thrown as exceptions, but syntax errors
   are not and propagated errors might not be exceptions.
*/
static int s2_eval_to_array( s2_engine * se, s2_ptoker * pr, cwal_array * dest ){
  int rc = 0;
  s2_op const * opComma = s2_ttype_op(S2_T_Comma);
  s2_ptoken prev = s2_ptoken_empty;
  char const * errMsg = 0;
  assert(!se->skipLevel);

  while( !rc ){
    s2_ptoken next = s2_ptoken_empty;
    cwal_value * v = 0;
    if( (rc = s2_eval_expr_impl( se, pr, opComma, 0, &v)) ) break;
    if(!v){
      if(s2_ptoker_is_eof(pr)){
        if(S2_T_Comma==prev.ttype){







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>




















>







26532
26533
26534
26535
26536
26537
26538
26539
26540
26541
26542
26543
26544
26545
26546
26547
26548
26549
26550
26551
26552
26553
26554
26555
26556
26557
26558
26559
26560
26561
26562
26563
26564
26565
26566
26567
26568
26569
26570
26571
26572
26573
26574
26575
26576
26577
26578
26579
26580
26581
26582
26583
26584
26585
26586
26587
26588
26589
26590
26591
26592
26593
26594
26595
26596
26597
26598
26599
26600
26601
26602
26603
26604
26605
26606
26607
26608
26609
26610
26611
26612
26613
26614
26615
26616
26617
26618
26619
26620
26621
26622
26623
26624
26625
26626
26627
26628
26629
26630
26631
26632
26633
      /* se->currentScript = oldScript; */
      s2_engine_subexpr_restore(se, &save);
      if(sub.errPos) pr->errPos = sub.errPos;
      return s2_check_interrupted(se, rc);
    }
  }
}

/**
   Like s2_eval_current_sub(se, pr, 0, rv), but pushes a scope
   and sets a local "this" variable to the self value.

   s2_ptoken_is_true_squiggly(&pr->token) MUST be true: this function
   assert()s it (only to simplify skip-mode handling, though we could
   extend it to handle other block-level constructs as well).

   ACHTUNG: self must not be a temporary value: it MUST have a ref
   or this will kill it. Exception: in skip-mode, self is not
   evaluated.
*/
static int s2_eval_current_sub_with_this( s2_engine * se, s2_ptoker * pr,
                                          cwal_value * self,
                                          cwal_value **rv ){
  assert( s2_ptoken_is_true_squiggly(&pr->token) );
  if(se->skipLevel){
    if(rv) *rv = cwal_value_undefined();
    return 0;
  }else{
    s2_scope sc = s2_scope_empty;
    cwal_value * xrv = 0;
    int rc = s2_scope_push(se, &sc);
    if(rc) return rc;
    rc = s2_var_decl_v(se, se->cache.keyThis, self, 0);
    if(!rc){
      rc = s2_eval_current_sub(se, pr, 0, rv ? &xrv : NULL);
    }
    s2_scope_pop2(se, rc ? 0 : xrv);
    if(!rc && rv) *rv = xrv
                    /* will be NULL on an empty expr except in skip-mode */;
    /*
      TODO: centralize this handling somewhere else? We should probably
      add it to object/array literals, etc.
    */
    if(rc){
      s2_keyword const * kword = 0;
      switch(rc){
        case CWAL_RC_RETURN:
          kword = s2_ttype_keyword(S2_T_KeywordReturn);
          assert(kword);
          s2_propagating_set(se, 0);
          break;
        case CWAL_RC_BREAK:
          kword = s2_ttype_keyword(S2_T_KeywordBreak);
          assert(kword);
          s2_propagating_set(se, 0);
          break;
        case CWAL_RC_CONTINUE:
          kword = s2_ttype_keyword(S2_T_KeywordContinue);
          assert(kword);
          break;
        default: break;
      }
      if(kword){
        rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX,
                           /* if we pass on the same rc, error reporting
                              won't DTRT. */
                           "Unhandled '%s' in script block.",
                           kword->word);
      }
    }
    return rc;
  }
}


int s2_eval_expr( s2_engine * se, s2_ptoker * st,
                  int evalFlags,
                  cwal_value ** rv){
  return s2_eval_expr_impl(se, st, 0, evalFlags, rv);
}

/**
   Expects pr to be a comma-separated list of expressions, until its
   EOF. Empty expressions and a stray trailing comma are not
   allowed. Evaluates each token and appends it to dest. Returns 0 on
   success. Some errors are thrown as exceptions, but syntax errors
   are not and propagated errors might not be exceptions.
*/
static int s2_eval_to_array( s2_engine * se, s2_ptoker * pr, cwal_array * dest ){
  int rc = 0;
  s2_op const * opComma = s2_ttype_op(S2_T_Comma);
  s2_ptoken prev = s2_ptoken_empty;
  char const * errMsg = 0;
  assert(!se->skipLevel);
  /* s2_dotop_state(se, 0, 0, 0); */
  while( !rc ){
    s2_ptoken next = s2_ptoken_empty;
    cwal_value * v = 0;
    if( (rc = s2_eval_expr_impl( se, pr, opComma, 0, &v)) ) break;
    if(!v){
      if(s2_ptoker_is_eof(pr)){
        if(S2_T_Comma==prev.ttype){
26908
26909
26910
26911
26912
26913
26914
26915
26916
26917
26918
26919
26920
26921
26922
26923
26924
26925
26926
26927
26928
26929
26930
26931
  cwal_value * fv = 0;
  cwal_value * vSelf = 0;
  s2_scope _SCOPE = s2_scope_empty;
  s2_scope * scope = &_SCOPE;
  s2_strace_entry strace = s2_strace_entry_empty;
  cwal_function * theFunc;
  s2_func_state * fst = 0;

  /* cwal_value * vKey; */
  assert(S2_T_ParenGroup==pr->token.ttype);

  if(!se->skipLevel){
    rc = s2_scope_push(se, scope);
    if(rc) return rc;
  }


  /* Check if this is a FUNC() or OBJ.FUNC() call... */
  opTok = s2_engine_peek_op(se);
  op = opTok ? s2_ttype_op( opTok->ttype ) : 0;

  /*
    To consider: if dot is not a dot op but se->dotOpLhs
    is set, then some LHS (possibly a (subexpr)) possibly







<








|







27204
27205
27206
27207
27208
27209
27210

27211
27212
27213
27214
27215
27216
27217
27218
27219
27220
27221
27222
27223
27224
27225
27226
  cwal_value * fv = 0;
  cwal_value * vSelf = 0;
  s2_scope _SCOPE = s2_scope_empty;
  s2_scope * scope = &_SCOPE;
  s2_strace_entry strace = s2_strace_entry_empty;
  cwal_function * theFunc;
  s2_func_state * fst = 0;

  /* cwal_value * vKey; */
  assert(S2_T_ParenGroup==pr->token.ttype);

  if(!se->skipLevel){
    rc = s2_scope_push(se, scope);
    if(rc) return rc;
  }

  if(rv) *rv = 0;
  /* Check if this is a FUNC() or OBJ.FUNC() call... */
  opTok = s2_engine_peek_op(se);
  op = opTok ? s2_ttype_op( opTok->ttype ) : 0;

  /*
    To consider: if dot is not a dot op but se->dotOpLhs
    is set, then some LHS (possibly a (subexpr)) possibly
26971
26972
26973
26974
26975
26976
26977
26978
26979
26980
26981
26982
26983
26984
26985
26986

26987
26988
26989
26990
26991
26992
26993
26994
26995
26996
26997
26998
      assignment ops as well, so that (x.y)=3 could work.
    */
    /* MARKER(("Possibly stealing a 'this'\n")); */
    assert(!fv);
    assert(!vSelf);
    vSelf = se->dotOpSelf;
    /* s2_dump_val(vSelf, "se->dotOpThis"); */
    se->dotOpSelf = se->dotOpKey = se->dotOpLhs = 0;
    fv = s2_engine_pop_value(se) /* presumably a dot-op result */;
    assert(fv);
    if(vSelf) cwal_value_ref( vSelf );
    cwal_value_ref( fv );
  }else{
    fv = s2_engine_pop_value(se) /* hopefully a function */;
    cwal_value_ref( fv );
  }


  /* In skip mode, we're done. We had to get the values off the stack,
     though, so we couldn't do this first. */
  if(se->skipLevel>0){
    *rv = cwal_value_undefined();
    goto end;
  }

  rc = s2_strace_push_pos(se, pr, origin.begin, &strace )
    /* we have to do this fairly late so that the
       the (still pending) call doesn't confuse the
       the trace. */







|


<





>




|







27266
27267
27268
27269
27270
27271
27272
27273
27274
27275

27276
27277
27278
27279
27280
27281
27282
27283
27284
27285
27286
27287
27288
27289
27290
27291
27292
27293
      assignment ops as well, so that (x.y)=3 could work.
    */
    /* MARKER(("Possibly stealing a 'this'\n")); */
    assert(!fv);
    assert(!vSelf);
    vSelf = se->dotOpSelf;
    /* s2_dump_val(vSelf, "se->dotOpThis"); */
    if(vSelf) cwal_value_ref(vSelf);
    fv = s2_engine_pop_value(se) /* presumably a dot-op result */;
    assert(fv);

    cwal_value_ref( fv );
  }else{
    fv = s2_engine_pop_value(se) /* hopefully a function */;
    cwal_value_ref( fv );
  }
  s2_dotop_state(se, 0, 0, 0);

  /* In skip mode, we're done. We had to get the values off the stack,
     though, so we couldn't do this first. */
  if(se->skipLevel>0){
    if(rv) *rv = cwal_value_undefined();
    goto end;
  }

  rc = s2_strace_push_pos(se, pr, origin.begin, &strace )
    /* we have to do this fairly late so that the
       the (still pending) call doesn't confuse the
       the trace. */
27016
27017
27018
27019
27020
27021
27022
27023
27024
27025
27026
27027
27028
27029
27030
  if(fst && (fst->flags & S2_FUNCSTATE_F_EMPTY_BODY
             && fst->flags & S2_FUNCSTATE_F_EMPTY_PARAMS)){
      /* If a script function has neither parameters nor a body, we
         don't have to do any of the following work. However, in order
         to get fst, we have to get theFunc, which requires setting up
         a scope and all that.
      */
    *rv = cwal_value_undefined();
  }else{
    /* 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;
    cwal_array * ar = cwal_new_array(se->e);







|







27311
27312
27313
27314
27315
27316
27317
27318
27319
27320
27321
27322
27323
27324
27325
  if(fst && (fst->flags & S2_FUNCSTATE_F_EMPTY_BODY
             && fst->flags & S2_FUNCSTATE_F_EMPTY_PARAMS)){
      /* If a script function has neither parameters nor a body, we
         don't have to do any of the following work. However, in order
         to get fst, we have to get theFunc, which requires setting up
         a scope and all that.
      */
    if(rv) *rv = cwal_value_undefined();
  }else{
    /* 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;
    cwal_array * ar = cwal_new_array(se->e);
27081
27082
27083
27084
27085
27086
27087

27088

27089
27090
27091
27092
27093
27094
27095

27096
27097
27098
27099
27100
27101
27102
27103
27104
27105
27106
27107
27108

27109
27110



27111
27112
27113
27114
27115
27116
27117
27118
27119
27120
27121
27122
27123
27124
27125
27126
27127
27128
27129
27130
27131
27132
27133
27134
         use/abuse it in the docs.
       */;
    se->currentScriptFunc = oldScriptFunc;
    assert(!se->callArgV && "Gets unset via the post-call() hook");
    se->callArgV = oldArgV;
    /* assert(0==oldArgV); */
    cwal_value_make_vacuum_proof(arV, 0);

    cwal_value_unhand(arV)

      /* Need unhand instead of unref in case this array is the value
         being returned. Array entry removal will handle the case that
         the returned value is a member of that array which was upscoped
         by the call().
      */;
  }
  end:

  rc = s2_check_interrupted(se, rc);
  switch(rc){
    case 0:
      if( !*rv ) *rv = cwal_value_undefined();
      break;
    case CWAL_RC_BREAK:
    case CWAL_RC_CONTINUE:
      assert(se->err.code);
      rc = s2_throw_err(se, 0, s2_ptoker_name_first(pr, 0), 0, 0);
      break;
#if 0
    case CWAL_RC_RETURN:
      rc = 0;

      *rv = s2_propagating_take(se);
      assert(*rv);



      break;
#endif
    case CWAL_RC_EXCEPTION:
      s2_exception_add_script_props(se, pr)
        /* Needed to decorate calls to native functions. */;
      /* fall through */
    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? */
      *rv = 0;
      break;
    default:
      *rv = 0;
      if(se->err.code){
        /* Likely a syntax-related error pending */
        rc = s2_throw_err(se, 0, 0, 0, 0);
      }else{
#if 1
        rc = s2_throw_ptoker(se, pr, rc,
                             "Function call returned non-exception "







>
|
>
|
<
<
<
<


>



|









>
|
|
>
>
>













|


|







27376
27377
27378
27379
27380
27381
27382
27383
27384
27385
27386




27387
27388
27389
27390
27391
27392
27393
27394
27395
27396
27397
27398
27399
27400
27401
27402
27403
27404
27405
27406
27407
27408
27409
27410
27411
27412
27413
27414
27415
27416
27417
27418
27419
27420
27421
27422
27423
27424
27425
27426
27427
27428
27429
27430
27431
27432
         use/abuse it in the docs.
       */;
    se->currentScriptFunc = oldScriptFunc;
    assert(!se->callArgV && "Gets unset via the post-call() hook");
    se->callArgV = oldArgV;
    /* assert(0==oldArgV); */
    cwal_value_make_vacuum_proof(arV, 0);
    if(!rc && rv && *rv){
        cwal_value_ref(*rv);
    }
    cwal_value_unref(arV);




  }
  end:
  /* s2_dotop_state( se, 0, 0, 0 ); */
  rc = s2_check_interrupted(se, rc);
  switch(rc){
    case 0:
      if( rv && !*rv ) *rv = cwal_value_undefined();
      break;
    case CWAL_RC_BREAK:
    case CWAL_RC_CONTINUE:
      assert(se->err.code);
      rc = s2_throw_err(se, 0, s2_ptoker_name_first(pr, 0), 0, 0);
      break;
#if 0
    case CWAL_RC_RETURN:
      rc = 0;
      if(rv){
          *rv = s2_propagating_take(se);
          assert(*rv);
          cwal_value_ref(*rv);
      }
      else cwal_propagating_set(se, NULL);
      break;
#endif
    case CWAL_RC_EXCEPTION:
      s2_exception_add_script_props(se, pr)
        /* Needed to decorate calls to native functions. */;
      /* fall through */
    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? */
      if(rv) *rv = 0;
      break;
    default:
      if(rv) *rv = 0;
      if(se->err.code){
        /* Likely a syntax-related error pending */
        rc = s2_throw_err(se, 0, 0, 0, 0);
      }else{
#if 1
        rc = s2_throw_ptoker(se, pr, rc,
                             "Function call returned non-exception "
27149
27150
27151
27152
27153
27154
27155




27156
27157
27158
27159
27160
27161
27162
27163
27164
27165





27166
27167
27168






















27169
27170
27171
27172
27173
27174
27175
#endif
      }
      break;
  }
  if(strace.pr){
    s2_strace_pop(se);
  }




  if(vSelf) cwal_value_unhand(vSelf);
  if(fv) cwal_value_unhand(fv);
  if(scope->parent){
    if(!rc){
      assert(*rv);
    }
    assert(scope == se->currentScope);
    s2_scope_pop2(se, rc ? 0 : *rv);
    assert(scope != se->currentScope);
  }





  return rc;
}
























/**
   Internal impl of s2_eval_expr().

   If fromLhsOp is not 0, this call is assumed to be the RHS of that
   operator. Upon encountering an operator of that precedence or less,
   that operator is put back into the tokenizer and evaluation







>
>
>
>
|
|

<
<
<

|


>
>
>
>
>



>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







27447
27448
27449
27450
27451
27452
27453
27454
27455
27456
27457
27458
27459
27460



27461
27462
27463
27464
27465
27466
27467
27468
27469
27470
27471
27472
27473
27474
27475
27476
27477
27478
27479
27480
27481
27482
27483
27484
27485
27486
27487
27488
27489
27490
27491
27492
27493
27494
27495
27496
27497
27498
27499
27500
27501
#endif
      }
      break;
  }
  if(strace.pr){
    s2_strace_pop(se);
  }
  if(!rc && rv && *rv){
      assert(cwal_value_refcount(*rv)>0 /* we ref'd it above */
             || cwal_value_is_builtin(*rv));
  }
  if(vSelf) cwal_value_unref(vSelf);
  if(fv) cwal_value_unref(fv);
  if(scope->parent){



    assert(scope == se->currentScope);
    s2_scope_pop2(se, rc ? 0 : (rv ? *rv : 0));
    assert(scope != se->currentScope);
  }
  if(!rc && rv && *rv){
      assert(cwal_value_refcount(*rv)>0 /* we ref'd it above */
             || cwal_value_is_builtin(*rv));
      cwal_value_unhand(*rv);
  }
  return rc;
}

/**
   If true, s2_eval_expr_impl() internally uses a local
   array to keep references active and vacuum-proofed.

   HOLY COW... when recycling is disabled, this causes alloc counts
   and total (not peak) memory to skyrocket (nearly 60% increase in
   the 20160131 unit tests). With recycling on alloc counts go up by
   roughly 3% but peak memory goes up anywhere from 1k to 7k (as the
   recycling bins accumulate) in those same unit tests.

   After changing this, MAKE SURE to run all the valgrind tests with
   various combinations of recycling and string interning!!!
*/
#define EVAL_USE_HOLDER 0
#if EVAL_USE_HOLDER
static int s2_eval_hold(s2_engine * se, cwal_array * tgt,
                        cwal_value * v){
  return cwal_value_is_builtin(v)
    ? 0
    : cwal_array_append(tgt, v);
}
#endif

/**
   Internal impl of s2_eval_expr().

   If fromLhsOp is not 0, this call is assumed to be the RHS of that
   operator. Upon encountering an operator of that precedence or less,
   that operator is put back into the tokenizer and evaluation
27213
27214
27215
27216
27217
27218
27219




27220
27221
27222
27223
27224
27225
27226
27227
27228
27229
27230
27231
27232
27233
  int totalValCount = 0 /* just for some error checks */;
  int nextTokenFlags = /* The s2_next_token() flags for the very next token */
    (evalFlags & S2_EVAL_NO_SKIP_FIRST_EOL/* fromLhsOp && S2_T_RHSEvalNoEOL!=fromLhsOp->id */)
    ? S2_NEXT_NO_SKIP_EOL
    : 0;
  s2_scope _SCOPE = s2_scope_empty /* cwal stack, if (flags&S2_EVAL_PUSH_SCOPE) */;
  s2_scope * scope = &_SCOPE;




#ifndef NDEBUG
  /* Just for sanity checking */
  int const oldValCount = se->st.vals.size;
  int const oldOpCount = se->st.ops.size;
#endif
  if( se->flags.interrupted ) return se->flags.interrupted;
  /* se->dotOpLhs = 0; */
  if(S2_EVAL_PUSH_SCOPE & evalFlags){
    MARKER(("Oh, this scope-pushing bit in eval_expr_impl IS used.\n"));
    assert(!"This isn't used, is it?");
    rc = s2_scope_push(se, scope);
    if(rc) return rc;
    assert(scope->parent);
    assert(0==se->sguard->sweep);







>
>
>
>






|







27539
27540
27541
27542
27543
27544
27545
27546
27547
27548
27549
27550
27551
27552
27553
27554
27555
27556
27557
27558
27559
27560
27561
27562
27563
  int totalValCount = 0 /* just for some error checks */;
  int nextTokenFlags = /* The s2_next_token() flags for the very next token */
    (evalFlags & S2_EVAL_NO_SKIP_FIRST_EOL/* fromLhsOp && S2_T_RHSEvalNoEOL!=fromLhsOp->id */)
    ? S2_NEXT_NO_SKIP_EOL
    : 0;
  s2_scope _SCOPE = s2_scope_empty /* cwal stack, if (flags&S2_EVAL_PUSH_SCOPE) */;
  s2_scope * scope = &_SCOPE;
#if EVAL_USE_HOLDER
  cwal_array * holder = 0 /* holds pending expression values to keep
                             them referenced and vacuum-safe. */;
#endif
#ifndef NDEBUG
  /* Just for sanity checking */
  int const oldValCount = se->st.vals.size;
  int const oldOpCount = se->st.ops.size;
#endif
  if( se->flags.interrupted ) return se->flags.interrupted;
  else if(!fromLhsOp) s2_dotop_state(se, 0, 0, 0);
  if(S2_EVAL_PUSH_SCOPE & evalFlags){
    MARKER(("Oh, this scope-pushing bit in eval_expr_impl IS used.\n"));
    assert(!"This isn't used, is it?");
    rc = s2_scope_push(se, scope);
    if(rc) return rc;
    assert(scope->parent);
    assert(0==se->sguard->sweep);
27241
27242
27243
27244
27245
27246
27247





























27248
27249
27250
27251
27252
27253
27254

  if(++se->metrics.subexpDepth > se->metrics.peakSubexpDepth){
    se->metrics.peakSubexpDepth = se->metrics.subexpDepth;
  }

  st->errPos = 0;






























  /* if(rv) *rv = 0; */
  if((S2_EVAL_PRE_SWEEP & evalFlags)
     && !fromLhsOp
     && !se->st.vals.size
     && !se->st.ops.size
     ){
    /* MARKER(("SWEEPING from eval_expr_impl\n")); */







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







27571
27572
27573
27574
27575
27576
27577
27578
27579
27580
27581
27582
27583
27584
27585
27586
27587
27588
27589
27590
27591
27592
27593
27594
27595
27596
27597
27598
27599
27600
27601
27602
27603
27604
27605
27606
27607
27608
27609
27610
27611
27612
27613

  if(++se->metrics.subexpDepth > se->metrics.peakSubexpDepth){
    se->metrics.peakSubexpDepth = se->metrics.subexpDepth;
  }

  st->errPos = 0;

#if EVAL_USE_HOLDER
#define eval_hold(V) if( holder && (rc = s2_eval_hold(se, holder, (V)))) goto end
#else
#define eval_hold(V) (void)0
#endif

#if EVAL_USE_HOLDER
  /**
    Reminder to self: we have to explicitly ref/unhand
    se->dotOpSelf/dotOpLhs/dotOpKey to keep them alive
    (i.e. to keep holder from cleaning them up) after this op
    returns (needed by 'unset' and possibly others).
  */
  holder = cwal_new_array(se->e);
  if(!holder){
    rc = CWAL_RC_OOM;
    goto end;
  }else{
    cwal_value * hv = cwal_array_value(holder);
    /* ++se->sguard->vacuum; */
    cwal_value_rescope(&se->currentScope->scope, hv)
        /* in case a cwal-level API has pushed a scope without us
         knowing */;
    cwal_value_ref(hv);
    cwal_value_make_vacuum_proof(hv,1);
  }
#endif
/* ^^^^ EVAL_USE_HOLDER */

  /* if(rv) *rv = 0; */
  if((S2_EVAL_PRE_SWEEP & evalFlags)
     && !fromLhsOp
     && !se->st.vals.size
     && !se->st.ops.size
     ){
    /* MARKER(("SWEEPING from eval_expr_impl\n")); */
27424
27425
27426
27427
27428
27429
27430

27431
27432
27433
27434
27435
27436
27437
          break;
        }
        lhs = 0;
        rc = s2_eval_ternary(se, st, &lhs);
        if(rc) break;
        else{
          assert(lhs);

          continue;
        }
      }
      case S2_T_Identifier:{
        /* If an Identifier is the RHS of a dot operator, change its
           type to PropertyKey so that identifier expansion is not
           done for this property access. The raw token bytes







>







27783
27784
27785
27786
27787
27788
27789
27790
27791
27792
27793
27794
27795
27796
27797
          break;
        }
        lhs = 0;
        rc = s2_eval_ternary(se, st, &lhs);
        if(rc) break;
        else{
          assert(lhs);
          eval_hold(lhs);
          continue;
        }
      }
      case S2_T_Identifier:{
        /* If an Identifier is the RHS of a dot operator, change its
           type to PropertyKey so that identifier expansion is not
           done for this property access. The raw token bytes
27483
27484
27485
27486
27487
27488
27489
27490
27491
27492
27493
27494
27495
27496
27497
        break;
    }
    if(se->flags.interrupted && !rc) rc = se->flags.interrupted;
    if(rc || doBreak) break;

    op = s2_ttype_op( pt.ttype );
    if(op && !fromLhsOp){
      se->dotOpSelf = se->dotOpKey = se->dotOpLhs = 0
        /* If these get stale, Very Bad Things happen in the cwal core
             (assertions, if we're lucky). */;
      /* se->dotOpId = 0 */ /* keep Other Bad Things from happening */;
      /* Reminder: this means that assignment ops cannot tell
         which dot-like op triggered them. */
    }








|







27843
27844
27845
27846
27847
27848
27849
27850
27851
27852
27853
27854
27855
27856
27857
        break;
    }
    if(se->flags.interrupted && !rc) rc = se->flags.interrupted;
    if(rc || doBreak) break;

    op = s2_ttype_op( pt.ttype );
    if(op && !fromLhsOp){
      s2_dotop_state( se, 0, 0, 0 )
        /* If these get stale, Very Bad Things happen in the cwal core
             (assertions, if we're lucky). */;
      /* se->dotOpId = 0 */ /* keep Other Bad Things from happening */;
      /* Reminder: this means that assignment ops cannot tell
         which dot-like op triggered them. */
    }

27717
27718
27719
27720
27721
27722
27723


27724
27725
27726
27727
27728
27729
27730
            if(S2_T_OpOr3 != pt.ttype && S2_T_OpElvis != pt.ttype){
              /*
                Clean this up:
                s2> x = 0; for( var i = 0; i < 100; (i++, x++ || 1, ++i) );
                MARKER: s2.c:296:s2_engine_sweep():	Swept up 48 value(s) in sweep mode
              */
              cwal_refunref(lhs);


            }
            continue;
          }
        }/*if(shortIt)*/
        break;
      }/* end &&, ||, ||| */
      case S2_T_Identifier:{







>
>







28077
28078
28079
28080
28081
28082
28083
28084
28085
28086
28087
28088
28089
28090
28091
28092
            if(S2_T_OpOr3 != pt.ttype && S2_T_OpElvis != pt.ttype){
              /*
                Clean this up:
                s2> x = 0; for( var i = 0; i < 100; (i++, x++ || 1, ++i) );
                MARKER: s2.c:296:s2_engine_sweep():	Swept up 48 value(s) in sweep mode
              */
              cwal_refunref(lhs);
            }else{
              eval_hold(lhs);
            }
            continue;
          }
        }/*if(shortIt)*/
        break;
      }/* end &&, ||, ||| */
      case S2_T_Identifier:{
27741
27742
27743
27744
27745
27746
27747

27748
27749
27750
27751
27752
27753
27754
            s2_op const * kOp = s2_ttype_op(kword->id);
            assert(kOp);
            pt.ttype = kword->id;
            op = kOp;
          }else{
            pt.ttype = st->token.ttype = kword->id;
            rc = kword->call(kword, se, st, &tVal);

            rc = s2_check_interrupted(se, rc);
            if(!rc && !tVal){
              st->errPos = pt.begin;
              rc = s2_err_ptoker(se, st, CWAL_RC_ASSERT,
                                 "Keyword '%s' eval'd to <NULL>, "
                                 "which is currently verboten",
                                 kword->word);







>







28103
28104
28105
28106
28107
28108
28109
28110
28111
28112
28113
28114
28115
28116
28117
            s2_op const * kOp = s2_ttype_op(kword->id);
            assert(kOp);
            pt.ttype = kword->id;
            op = kOp;
          }else{
            pt.ttype = st->token.ttype = kword->id;
            rc = kword->call(kword, se, st, &tVal);
            s2_dotop_state( se, 0, 0, 0 );
            rc = s2_check_interrupted(se, rc);
            if(!rc && !tVal){
              st->errPos = pt.begin;
              rc = s2_err_ptoker(se, st, CWAL_RC_ASSERT,
                                 "Keyword '%s' eval'd to <NULL>, "
                                 "which is currently verboten",
                                 kword->word);
27818
27819
27820
27821
27822
27823
27824
27825
27826
27827
27828

27829
27830
27831
27832
27833
27834
27835
27836
27837
27838
27839
27840
27841
27842
27843
27844
27845
27846
27847
27848
27849
27850
              rc = s2_throw_ptoker(se, st, CWAL_RC_NOT_FOUND,
                                   "Could not resolve identifier "
                                   "'%.*s'",
                                   tlen, pt.begin);
            }
          }else if(prevOp && s2_ttype_is_identifier_prefix(prevOp->id)){
            /* Previous token was prefix ++/-- (or similar) */
#if 1
            if(!s2_next_is_dotish(se, st)){
              tVal = cwal_new_string_value(se->e, pt.begin, (cwal_size_t)tlen);
              if(!tVal){

                rc = CWAL_RC_OOM;
              }
            }else{
              pt.ttype = S2_T_Value;              
            }
#else
            if(cwal_props_can(tVal)){
              pt.ttype = S2_T_Value;
            }else{
              tVal = cwal_new_string_value(se->e, pt.begin, (cwal_size_t)tlen);
              if(!tVal){
                rc = CWAL_RC_OOM;
              }
            }
#endif
          }else if(s2_next_wants_identifier(se, st)){
            /* Required so that assignment gets the identifier's string value,
               but we let it get validated above so that we get precise
               error location info. We just hope that pending ops don't
               invalidate it (potential corner case?).
            */
            tVal = 0;







<

|

>
|




<
<
<
<
<
<
<
<
<
<







28181
28182
28183
28184
28185
28186
28187

28188
28189
28190
28191
28192
28193
28194
28195
28196










28197
28198
28199
28200
28201
28202
28203
              rc = s2_throw_ptoker(se, st, CWAL_RC_NOT_FOUND,
                                   "Could not resolve identifier "
                                   "'%.*s'",
                                   tlen, pt.begin);
            }
          }else if(prevOp && s2_ttype_is_identifier_prefix(prevOp->id)){
            /* Previous token was prefix ++/-- (or similar) */

            if(!s2_next_is_dotish(se, st)){
              rc = s2_ptoken_create_value(se, &pt, &tVal);
              if(!tVal){
                assert(rc);
                /* rc = CWAL_RC_OOM; */
              }
            }else{
              pt.ttype = S2_T_Value;              
            }










          }else if(s2_next_wants_identifier(se, st)){
            /* Required so that assignment gets the identifier's string value,
               but we let it get validated above so that we get precise
               error location info. We just hope that pending ops don't
               invalidate it (potential corner case?).
            */
            tVal = 0;
28018
28019
28020
28021
28022
28023
28024

28025
28026
28027
28028
28029
28030
28031
28032
28033
28034
28035

28036
28037
28038
28039
28040
28041
28042
28043
          break;
        }
#endif
        assert(pt.adjBegin);
        assert(pt.adjEnd);
        assert(pt.adjEnd >= pt.adjBegin);
        if(S2_T_ParenGroup==pt.ttype){

          if(s2_looks_like_fcall(se, st,
                                 (S2_EVAL_STOP_AT_CALL & evalFlags),
                                 prevOp, &rc) && !rc){
            if(S2_EVAL_STOP_AT_CALL & evalFlags){
              doBreak = 1;
            }else{
              /* MARKER(("Looks like func call? rc=%s prevOp=%s\n",
                 s2_rc_cstr(rc), prevOp?prevOp->sym:"<NULL>")); */
              rc = s2_eval_fcall(se, st, &tVal);
            }
            break;

          }else if(rc) break;
        }
        if(pt.adjEnd > pt.adjBegin){
          /* In order to know whether it's really empty, and fail
             consistently across both skipping and non-skipping mode,
             we have to parse the subexpr regardless of skip level
             :/.
          */







>











>
|







28371
28372
28373
28374
28375
28376
28377
28378
28379
28380
28381
28382
28383
28384
28385
28386
28387
28388
28389
28390
28391
28392
28393
28394
28395
28396
28397
28398
          break;
        }
#endif
        assert(pt.adjBegin);
        assert(pt.adjEnd);
        assert(pt.adjEnd >= pt.adjBegin);
        if(S2_T_ParenGroup==pt.ttype){
          doBreak = 0;
          if(s2_looks_like_fcall(se, st,
                                 (S2_EVAL_STOP_AT_CALL & evalFlags),
                                 prevOp, &rc) && !rc){
            if(S2_EVAL_STOP_AT_CALL & evalFlags){
              doBreak = 1;
            }else{
              /* MARKER(("Looks like func call? rc=%s prevOp=%s\n",
                 s2_rc_cstr(rc), prevOp?prevOp->sym:"<NULL>")); */
              rc = s2_eval_fcall(se, st, &tVal);
            }
            break;
          }
          else if(rc) break;
        }
        if(pt.adjEnd > pt.adjBegin){
          /* In order to know whether it's really empty, and fail
             consistently across both skipping and non-skipping mode,
             we have to parse the subexpr regardless of skip level
             :/.
          */
28296
28297
28298
28299
28300
28301
28302

28303
28304
28305
28306
28307
28308
28309
28310
28311
28312
28313
28314
28315
28316
28317
28318
28319
28320
28321
        if((se->flags.traceStack>0) && (S2_T_Identifier==pt.ttype)){
          MARKER(("Identifier token: %.*s\n", tlen, pt.begin));
        }
        vtok = s2_engine_push_tv(se, pt.ttype, tVal);
        if(!vtok){
          rc = CWAL_RC_OOM;
        }else{

          ++totalValCount;
          vtok->srcPos = pt.begin;
          if(se->skipLevel>0){
#if 1
            /* s2_dump_val(tVal, "what is this"); */
            assert((tVal == cwal_value_undefined())
                   && "current internal convention for "
                   "skip-mode evaluation was violated");
#endif
          }
        }
      }
    }/*if op else value */
  }/* for-each token */

  if(rc && !st->errPos){
    st->errPos = (pt.begin && (pt.begin != pt.end)) ? pt.begin : prevTok.begin;
  }else if(!rc){
    assert(capBegin);







>











|







28651
28652
28653
28654
28655
28656
28657
28658
28659
28660
28661
28662
28663
28664
28665
28666
28667
28668
28669
28670
28671
28672
28673
28674
28675
28676
28677
        if((se->flags.traceStack>0) && (S2_T_Identifier==pt.ttype)){
          MARKER(("Identifier token: %.*s\n", tlen, pt.begin));
        }
        vtok = s2_engine_push_tv(se, pt.ttype, tVal);
        if(!vtok){
          rc = CWAL_RC_OOM;
        }else{
          eval_hold(tVal);
          ++totalValCount;
          vtok->srcPos = pt.begin;
          if(se->skipLevel>0){
#if 1
            /* s2_dump_val(tVal, "what is this"); */
            assert((tVal == cwal_value_undefined())
                   && "current internal convention for "
                   "skip-mode evaluation was violated");
#endif
          }
        }
      }/*if(!rc)*/
    }/*if op else value */
  }/* for-each token */

  if(rc && !st->errPos){
    st->errPos = (pt.begin && (pt.begin != pt.end)) ? pt.begin : prevTok.begin;
  }else if(!rc){
    assert(capBegin);
28393
28394
28395
28396
28397
28398
28399

28400
28401
28402
28403
28404
28405
28406
28407
28408
28409
28410
28411
28412
28413
28414
28415
28416
28417
28418





















28419
28420
28421
28422
28423
28424
28425
      s2_dump_val(xrv, "eval_expr result");
    }
    if(rv) *rv = xrv;
    else cwal_refunref(xrv);
  }

  end:

  if(rc){
    rc= 1 ? rc : 0 /* put breakpoint here (the ?: is to avoid
                      assignment-to-self warning from clang).*/;
  }
  /* Clean up... */
  if(!fromLhsOp){
    /* We must clear these avoid picking them up after they're stale.
       However, they's needed by, e.g. (unset x.y).
    */
    se->dotOpSelf = se->dotOpKey = se->dotOpLhs = 0;
  }
  if(rc && !se->err.code && st->errMsg
     && (CWAL_RC_EXCEPTION!=rc)){
    /* Error from the tokenizer. */
    rc = s2_err_ptoker(se, st, rc, 0);
  }

  rc = s2_rv_maybe_accept(se, scope->scope.parent /* 0 is okay */, rc, rv);






















  if(!evalIt){
    assert(se->skipLevel==1+oldSkipLevel);
    se->skipLevel = oldSkipLevel;
  }
  if(ownStack){
    s2_engine_stack_swap(se, &priorStack);
    s2_estack_clear(se, &priorStack, 1);







>









|









>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>







28749
28750
28751
28752
28753
28754
28755
28756
28757
28758
28759
28760
28761
28762
28763
28764
28765
28766
28767
28768
28769
28770
28771
28772
28773
28774
28775
28776
28777
28778
28779
28780
28781
28782
28783
28784
28785
28786
28787
28788
28789
28790
28791
28792
28793
28794
28795
28796
28797
28798
28799
28800
28801
28802
28803
      s2_dump_val(xrv, "eval_expr result");
    }
    if(rv) *rv = xrv;
    else cwal_refunref(xrv);
  }

  end:
#undef eval_hold
  if(rc){
    rc= 1 ? rc : 0 /* put breakpoint here (the ?: is to avoid
                      assignment-to-self warning from clang).*/;
  }
  /* Clean up... */
  if(!fromLhsOp){
    /* We must clear these avoid picking them up after they're stale.
       However, they's needed by, e.g. (unset x.y).
    */
    s2_dotop_state( se, 0, 0, 0 );
  }
  if(rc && !se->err.code && st->errMsg
     && (CWAL_RC_EXCEPTION!=rc)){
    /* Error from the tokenizer. */
    rc = s2_err_ptoker(se, st, rc, 0);
  }

  rc = s2_rv_maybe_accept(se, scope->scope.parent /* 0 is okay */, rc, rv);


#if EVAL_USE_HOLDER
  if(holder){
    cwal_value * hv = cwal_array_value(holder);
    /* --se->sguard->vacuum; */
    cwal_value_make_vacuum_proof(hv, 0);
    assert(1==cwal_value_refcount(hv));
    if(!rc && rv) cwal_value_ref(*rv);
    if(se->dotOpSelf) cwal_value_ref(se->dotOpSelf);
    if(se->dotOpLhs) cwal_value_ref(se->dotOpLhs);
    if(se->dotOpKey) cwal_value_ref(se->dotOpKey);
    cwal_value_unref(hv);
    holder = 0;
    if(!rc && rv) cwal_value_unhand(*rv);
    if(se->dotOpSelf) cwal_value_unhand(se->dotOpSelf);
    if(se->dotOpLhs) cwal_value_unhand(se->dotOpLhs);
    if(se->dotOpKey) cwal_value_unhand(se->dotOpKey);
  }
#endif
/* ^^^^ EVAL_USE_HOLDER */

  if(!evalIt){
    assert(se->skipLevel==1+oldSkipLevel);
    se->skipLevel = oldSkipLevel;
  }
  if(ownStack){
    s2_engine_stack_swap(se, &priorStack);
    s2_estack_clear(se, &priorStack, 1);
28444
28445
28446
28447
28448
28449
28450


28451
28452
28453
28454
28455
28456
28457
  }else{
    if(!scope->parent){
      --se->sguard->sweep;
    }
  }
  return rc;
}



int s2_eval_buffer( s2_engine * se,
                    char newScope,
                    char const * name,
                    cwal_buffer const * buf,
                    cwal_value **rv ){
  return s2_eval_cstr( se, newScope, name,







>
>







28822
28823
28824
28825
28826
28827
28828
28829
28830
28831
28832
28833
28834
28835
28836
28837
  }else{
    if(!scope->parent){
      --se->sguard->sweep;
    }
  }
  return rc;
}

#undef EVAL_USE_HOLDER

int s2_eval_buffer( s2_engine * se,
                    char newScope,
                    char const * name,
                    cwal_buffer const * buf,
                    cwal_value **rv ){
  return s2_eval_cstr( se, newScope, name,
29666
29667
29668
29669
29670
29671
29672
29673
29674
29675
29676
29677
29678
29679


29680
29681
29682
29683
29684
29685
29686
      else if(!se->skipLevel){
        if(!se->dotOpLhs || !se->dotOpKey){
          pr->errPos = ident.begin;
          return s2_throw_ptoker( se, pr, CWAL_SCR_SYNTAX,
                                  "Illegal RHS for %s operation.",
                                  kw->word);
        }
        cwal_value_ref(se->dotOpLhs);
        cwal_value_ref(se->dotOpKey);
        /* Do we want to support: unset hash # key? */
        s2_handle_set_result(se, pr,
                             s2_set_v( se, se->dotOpLhs, se->dotOpKey, 0 ));
        cwal_value_unhand(se->dotOpLhs);
        cwal_value_unhand(se->dotOpKey);


      }
      if(s2_ptoker_next_is_ttype(se, pr, 0, S2_T_Comma, 1)){
        goto next_part;
      }
      break;
    }
    default:







|
|

|
|
|
|
>
>







30046
30047
30048
30049
30050
30051
30052
30053
30054
30055
30056
30057
30058
30059
30060
30061
30062
30063
30064
30065
30066
30067
30068
      else if(!se->skipLevel){
        if(!se->dotOpLhs || !se->dotOpKey){
          pr->errPos = ident.begin;
          return s2_throw_ptoker( se, pr, CWAL_SCR_SYNTAX,
                                  "Illegal RHS for %s operation.",
                                  kw->word);
        }
        /* cwal_value_ref(se->dotOpLhs); */
        /* cwal_value_ref(se->dotOpKey); */
        /* Do we want to support: unset hash # key? */
        rc = s2_handle_set_result(se, pr,
                                  s2_set_v( se, se->dotOpLhs, se->dotOpKey, 0 ));
        /* cwal_value_unhand(se->dotOpLhs); */
        /* cwal_value_unhand(se->dotOpKey); */
        s2_dotop_state(se, 0, 0, 0);
        if(rc) return rc;
      }
      if(s2_ptoker_next_is_ttype(se, pr, 0, S2_T_Comma, 1)){
        goto next_part;
      }
      break;
    }
    default:
30754
30755
30756
30757
30758
30759
30760

30761
30762
30763
30764
30765
30766
30767

    */
    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(("Not a script function (or empty function). Not injecting argv/this.\n")); */

    return 0;
  }
#if 0
  else if(cwal_scope_search(args->scope, 0, "argv", 4, NULL)){
    /* argv is already set. This is (we hope) a callback which itself
       passed on a call via cwal_function_call_in_scope(). Do we have
       a better mechanism for avoiding a duplicate init in that case?







>







31136
31137
31138
31139
31140
31141
31142
31143
31144
31145
31146
31147
31148
31149
31150

    */
    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(("Not a script function (or empty function). Not injecting argv/this.\n")); */
    s2_dotop_state( se, 0, 0, 0 ) /* necessary!!! */;
    return 0;
  }
#if 0
  else if(cwal_scope_search(args->scope, 0, "argv", 4, NULL)){
    /* argv is already set. This is (we hope) a callback which itself
       passed on a call via cwal_function_call_in_scope(). Do we have
       a better mechanism for avoiding a duplicate init in that case?
30780
30781
30782
30783
30784
30785
30786


30787
30788
30789
30790
30791
30792
30793
    cwal_scope * s = args->scope;
    s2_engine * se = fs ? fs->se : s2_engine_from_state(args->engine);
    cwal_value * v;
    assert(fs && "Currently always true here.");
    assert(se);
    calleeV = cwal_function_value(args->callee);
    assert(calleeV);


    if(!fs /* native call, which _might_ eval script code */
       ||
       !(/* empty function */
         fs->flags & S2_FUNCSTATE_F_EMPTY_BODY
         && fs->flags & S2_FUNCSTATE_F_EMPTY_PARAMS)
       ){








>
>







31163
31164
31165
31166
31167
31168
31169
31170
31171
31172
31173
31174
31175
31176
31177
31178
    cwal_scope * s = args->scope;
    s2_engine * se = fs ? fs->se : s2_engine_from_state(args->engine);
    cwal_value * v;
    assert(fs && "Currently always true here.");
    assert(se);
    calleeV = cwal_function_value(args->callee);
    assert(calleeV);
    s2_dotop_state( se, 0, 0, 0 ) /* necessary!!! */;

    if(!fs /* native call, which _might_ eval script code */
       ||
       !(/* empty function */
         fs->flags & S2_FUNCSTATE_F_EMPTY_BODY
         && fs->flags & S2_FUNCSTATE_F_EMPTY_PARAMS)
       ){

30873
30874
30875
30876
30877
30878
30879
30880
30881
30882
30883
30884
30885
30886
30887
  /* s2_func_state * fs = (s2_func_state *)cwal_args_callee_state(args, &s2_func_state_empty); */
  assert(se);
  /* MARKER(("Callback post-hook\n")); */
  /* s2_dump_val(cwal_array_value(se->callArgV), "se->callArgV"); */
  se->callArgV = 0
    /* required for certain calling combinations of native vs script funcs
       to work. */;
  se->dotOpSelf = se->dotOpKey = se->dotOpLhs = 0 /* Just in case */;
  /**
     Reminder to self:

     We can potentially use this callback to communicate certain
     non-zero results back up the call chain, e.g. CWAL_RC_OOM could
     be stuffed into a dedicated error code propagation slot (the plan
     is to consolidate return/exit/etc. with the







<







31258
31259
31260
31261
31262
31263
31264

31265
31266
31267
31268
31269
31270
31271
  /* s2_func_state * fs = (s2_func_state *)cwal_args_callee_state(args, &s2_func_state_empty); */
  assert(se);
  /* MARKER(("Callback post-hook\n")); */
  /* s2_dump_val(cwal_array_value(se->callArgV), "se->callArgV"); */
  se->callArgV = 0
    /* required for certain calling combinations of native vs script funcs
       to work. */;

  /**
     Reminder to self:

     We can potentially use this callback to communicate certain
     non-zero results back up the call chain, e.g. CWAL_RC_OOM could
     be stuffed into a dedicated error code propagation slot (the plan
     is to consolidate return/exit/etc. with the
31142
31143
31144
31145
31146
31147
31148
31149
31150
31151
31152
31153
31154
31155
31156
31157
31158
31159
31160
31161
31162
31163
31164
31165
31166
31167
31168
31169
  if(!rc){
    cwal_value * ctorResult = 0 /* the ctor result */;
    cwal_array * oldArgV = se->callArgV
      /* not _actually_ sure this is needed, but there might
         be a corner case or two without it. */;
    se->callArgV = args;
    if(args) cwal_value_ref(cwal_array_value(args));
    cwal_container_client_flags_set(newThis, S2_VAL_F_IS_NEWING)
      /* we use this for implementing the "is-new-being-called?" operation.
         This is only "safe" as long as no ctors modify these flags. Hmm.
      */;
    assert(S2_VAL_F_IS_NEWING & cwal_container_client_flags_get(newThis));
    rc = args
      ? cwal_function_call_array(&sc.scope, ctor,
                                 newThis, &ctorResult, args)
      : cwal_function_call_in_scope(&sc.scope, ctor,
                                    newThis, &ctorResult, 0, NULL)
      ;
    assert(S2_VAL_F_IS_NEWING & cwal_container_client_flags_get(newThis));
    cwal_container_client_flags_set(newThis, ~S2_VAL_F_IS_NEWING &
                                    cwal_container_client_flags_get(newThis));
    assert(!se->callArgV && "callArgV Gets unset via the call() hook(s)");
    se->callArgV = oldArgV;
    if(args) cwal_value_unhand(cwal_array_value(args));
    if(!rc && ctorResult
       && newThis != ctorResult
       && cwal_value_undefined() != ctorResult){
      /*







<
<
|
<
<






<
<
|







31526
31527
31528
31529
31530
31531
31532


31533


31534
31535
31536
31537
31538
31539


31540
31541
31542
31543
31544
31545
31546
31547
  if(!rc){
    cwal_value * ctorResult = 0 /* the ctor result */;
    cwal_array * oldArgV = se->callArgV
      /* not _actually_ sure this is needed, but there might
         be a corner case or two without it. */;
    se->callArgV = args;
    if(args) cwal_value_ref(cwal_array_value(args));


    sc.newingThis = newThis;


    rc = args
      ? cwal_function_call_array(&sc.scope, ctor,
                                 newThis, &ctorResult, args)
      : cwal_function_call_in_scope(&sc.scope, ctor,
                                    newThis, &ctorResult, 0, NULL)
      ;


    sc.newingThis = 0;
    assert(!se->callArgV && "callArgV Gets unset via the call() hook(s)");
    se->callArgV = oldArgV;
    if(args) cwal_value_unhand(cwal_array_value(args));
    if(!rc && ctorResult
       && newThis != ctorResult
       && cwal_value_undefined() != ctorResult){
      /*
31212
31213
31214
31215
31216
31217
31218


31219
31220


31221
31222
31223
31224
31225
31226
31227
  s2_ptoken tIdent = s2_ptoken_empty;
  cwal_size_t idLen;
  cwal_value * operand = 0;
  cwal_function * ctor = 0;
  cwal_array * args = 0;
  cwal_value * vargs = 0;
  s2_ptoker ptArgs = s2_ptoker_empty;


  s2_strace_entry strace = s2_strace_entry_empty;
  s2_ptoken const origin = pr->token;


  rc = s2_strace_push_pos(se, pr, origin.begin, &strace )
    /* treat new() like a function for stack trace purposes
       or exceptions may contain far-off location info.
    */;
  if(rc) return rc /* after this, no more 'return', only goto end */;
  while(s2_is_space(*pr->token.end)){
    /* Cosmetic workaround! */







>
>


>
>







31590
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600
31601
31602
31603
31604
31605
31606
31607
31608
31609
  s2_ptoken tIdent = s2_ptoken_empty;
  cwal_size_t idLen;
  cwal_value * operand = 0;
  cwal_function * ctor = 0;
  cwal_array * args = 0;
  cwal_value * vargs = 0;
  s2_ptoker ptArgs = s2_ptoker_empty;
  s2_ptoken tTail = s2_ptoken_empty;
  s2_ptoken tWithThis = s2_ptoken_empty;
  s2_strace_entry strace = s2_strace_entry_empty;
  s2_ptoken const origin = pr->token;
  cwal_value * xrv = 0;
  assert(rv);
  rc = s2_strace_push_pos(se, pr, origin.begin, &strace )
    /* treat new() like a function for stack trace purposes
       or exceptions may contain far-off location info.
    */;
  if(rc) return rc /* after this, no more 'return', only goto end */;
  while(s2_is_space(*pr->token.end)){
    /* Cosmetic workaround! */
31235
31236
31237
31238
31239
31240
31241

31242
31243
31244
31245
31246
31247
31248
31249
31250
31251
31252
31253
31254
31255
31256
31257
31258
  else if(operand) cwal_value_ref(operand);
  if(!operand || S2_T_ParenGroup!=pr->token.ttype){
    rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX,
                       "Expecting EXPR(...) after '%s'.",
                       kw->word);
    goto end;
  }

  tIdent = pr->capture;
  idLen = (cwal_size_t)(tIdent.end-tIdent.begin);
#if 0
  MARKER(("Capture=%.*s\n", (int)(pr->capture.end-pr->capture.begin),
          pr->capture.begin));
  MARKER(("tIdent=%.*s\n", (int)idLen, tIdent.begin));
#endif
  if(se->skipLevel>0){
    *rv = cwal_value_undefined();
    goto end;
  }else if(!cwal_props_can(operand)){
    rc = s2_throw_ptoker(se, pr, CWAL_RC_EXCEPTION,
                         "'%s' expects a container, "
                         "but '%.*s' resolves to type '%s'.",
                         kw->word, (int)idLen, tIdent.begin,
                         cwal_value_type_name(operand));
    goto end;







>








|
|







31617
31618
31619
31620
31621
31622
31623
31624
31625
31626
31627
31628
31629
31630
31631
31632
31633
31634
31635
31636
31637
31638
31639
31640
31641
  else if(operand) cwal_value_ref(operand);
  if(!operand || S2_T_ParenGroup!=pr->token.ttype){
    rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX,
                       "Expecting EXPR(...) after '%s'.",
                       kw->word);
    goto end;
  }
  tTail = pr->token;
  tIdent = pr->capture;
  idLen = (cwal_size_t)(tIdent.end-tIdent.begin);
#if 0
  MARKER(("Capture=%.*s\n", (int)(pr->capture.end-pr->capture.begin),
          pr->capture.begin));
  MARKER(("tIdent=%.*s\n", (int)idLen, tIdent.begin));
#endif
  if(se->skipLevel>0){
    xrv = cwal_value_undefined();
    goto check_tail;
  }else if(!cwal_props_can(operand)){
    rc = s2_throw_ptoker(se, pr, CWAL_RC_EXCEPTION,
                         "'%s' expects a container, "
                         "but '%.*s' resolves to type '%s'.",
                         kw->word, (int)idLen, tIdent.begin,
                         cwal_value_type_name(operand));
    goto end;
31285
31286
31287
31288
31289
31290
31291
31292
31293
31294



















31295
31296
31297
31298
31299
31300
31301
31302
31303
31304
31305
31306
31307



31308



31309
31310
31311
31312
31313
31314
31315
  /* cwal_value_ref(operand); */
  s2_ptoker_sub_from_toker( &ptArgs, pr );
  rc = s2_eval_to_array( se, &ptArgs, args );
  /* cwal_value_unhand(operand); */
  /* cwal_value_make_vacuum_proof(operand, 0); */
  if(!rc){
    /* s2_dump_val(vargs,"call args"); */
    rc = s2_ctor_apply( se, operand, ctor, args, rv );
    /* s2_dump_val(*rv, "result from 'new'"); */
  }



















  end:
  assert( rc ? !*rv : !!*rv );
  if(strace.pr){
    s2_strace_pop(se);
  }
  if(operand){
    cwal_value_unref(operand);
    operand = 0;
  }
  if(vargs){
    cwal_value_make_vacuum_proof(vargs, 0);
    if(vargs != *rv) cwal_value_unref(vargs);
  }



  if(!rc) cwal_value_unhand(*rv);



  return rc;
}

int s2_keyword_f_enum( s2_keyword const * kw, s2_engine * se,
                       s2_ptoker * pr, cwal_value **rv){
  int rc;
  s2_ptoken tName = s2_ptoken_empty;







|
|

>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>

<









|

>
>
>
|
>
>
>







31668
31669
31670
31671
31672
31673
31674
31675
31676
31677
31678
31679
31680
31681
31682
31683
31684
31685
31686
31687
31688
31689
31690
31691
31692
31693
31694
31695
31696
31697

31698
31699
31700
31701
31702
31703
31704
31705
31706
31707
31708
31709
31710
31711
31712
31713
31714
31715
31716
31717
31718
31719
31720
31721
31722
  /* cwal_value_ref(operand); */
  s2_ptoker_sub_from_toker( &ptArgs, pr );
  rc = s2_eval_to_array( se, &ptArgs, args );
  /* cwal_value_unhand(operand); */
  /* cwal_value_make_vacuum_proof(operand, 0); */
  if(!rc){
    /* s2_dump_val(vargs,"call args"); */
    rc = s2_ctor_apply( se, operand, ctor, args, &xrv );
    /* s2_dump_val(xrv, "result from 'new'"); */
  }

  check_tail:
  if(rc){
    assert(!xrv);
  }else{
    if(xrv) cwal_value_ref(xrv);
    /* Check for a {body} part after the T(...), and treat it like
       an inlined extension to the ctor like in Java: new X() {{ ... }}.
    */
    s2_next_token(se, pr, 0, &tWithThis);
    if(s2_ptoken_is_true_squiggly(&tWithThis)){
      pr->token = tTail = tWithThis;
      /*MARKER(("Got post-new() body: %.*s\n",
        (int)(tTail.end-tTail.begin), tTail.begin));*/
      if(!se->skipLevel){
        rc = s2_eval_current_sub_with_this(se, pr, xrv, NULL);
      }
    }
  }
  end:

  if(strace.pr){
    s2_strace_pop(se);
  }
  if(operand){
    cwal_value_unref(operand);
    operand = 0;
  }
  if(vargs){
    cwal_value_make_vacuum_proof(vargs, 0);
    cwal_value_unref(vargs);
  }
  if(!rc){
    pr->token = tTail;
    *rv = xrv;
    cwal_value_unhand(xrv);
  }else if(xrv){
    cwal_value_unref(xrv);
  }
  return rc;
}

int s2_keyword_f_enum( s2_keyword const * kw, s2_engine * se,
                       s2_ptoker * pr, cwal_value **rv){
  int rc;
  s2_ptoken tName = s2_ptoken_empty;
31547
31548
31549
31550
31551
31552
31553



















































































































































31554
31555
31556
31557
31558
31559
31560
31561
31562
31563
31564
31565
31566
31567
31568
31569
31570
31571
31572
31573
31574
31575
31576
31577
31578
31579
31580
31581
31582
31583
31584
31585
31586
31587
31588
31589
31590
31591
31592
31593
31594
31595
31596
31597
31598
31599
31600
31601
31602
31603
31604
31605
31606
31607
31608
31609
31610
31611
31612
31613
31614
31615
31616
31617
31618
31619
31620
31621
31622
31623
31624
31625
31626
31627
31628
31629
31630
31631
31632
31633
31634
31635
31636
31637
31638
31639
31640
31641
31642
31643
31644
31645
31646
31647
31648
31649
31650
31651
31652
31653
31654
31655
31656
31657
31658
31659
31660
31661
31662
31663
31664
31665
31666
31667
31668
31669
31670
31671
31672
31673
31674
31675
31676
31677
31678

31679
31680
31681
31682
31683
31684
31685
31686
31687
31688
31689
31690
31691
31692
31693
31694
31695
31696
31697
31698
31699
31700
31701
31702
31703
31704
31705
31706
31707
31708
31709
31710
31711
31712
31713
31714
31715
31716
31717
31718
31719
31720
31721
31722
31723
31724
31725
31726
31727
31728
31729
31730
31731
31732
31733
31734
31735
31736
31737
31738
31739
31740
31741

31742
31743
31744
31745





31746
31747


31748
31749
31750
31751
31752
31753
31754
    cwal_container_client_flags_set(store, S2_VAL_F_ENUM);
    cwal_value_unhand(store);
    *rv = store;
  }
  return rc;
}





















































































































































int s2_keyword_f_typeinfo( s2_keyword const * kw, s2_engine * se,
                           s2_ptoker * pr, cwal_value **rv){
  s2_ptoken tIdent = s2_ptoken_empty;
  s2_ptoker prBody = s2_ptoker_empty;
  s2_ptoker const * oldScript = se->currentScript;
  cwal_size_t idLen = 0;
  cwal_value * xrv = 0;
  int rc;
  int buul = -1 /* <0=unknown, 0=false, >0=true */;
  enum TypeInfoWords {
    /* Sentinel value. Must be 0. */
    TYPEINFO_NONE = 0,
    /** True if operand can be used as an operand for the "new" keyword. */
    TYPEINFO_CANNEW,
    /** True if operand is or has an array in its prototype chain. */
    TYPEINFO_HASARRAY,
    /** True if operand is or has an enum in its prototype chain. */
    TYPEINFO_HASENUM,
    /** True if operand is or has an exception in its prototype chain. */
    TYPEINFO_HASEXCEPTION,
    /** True if operand is or has a hash in its prototype chain. */
    TYPEINFO_HASHASH,
    /** True if operand is or has a native in its prototype chain. */
    TYPEINFO_HASNATIVE,
    /** True if operand is or has an Object in its prototype chain. */
    TYPEINFO_HASOBJECT,
    /** True if operand has a prototype. */
    TYPEINFO_HASPROTOYPE,
    /** True if operand is an array. */
    TYPEINFO_ISARRAY,
    /** True if operand is a bool. */
    TYPEINFO_ISBOOL,
    /** True if operand is a buffer. */
    TYPEINFO_ISBUFFER,
    /** True if operand is callable (is a function or has a function
        in its prototype chain). */
    TYPEINFO_ISCALLABLE,
    /** True if operand is a container (can hold its own properties). */
    TYPEINFO_ISCONTAINER,
    /** True if operand is the name of a declared (currently in-scope) variable/const. */
    TYPEINFO_ISDECLARED,
    /** True if operand is legal for use with the dot operator. */
    TYPEINFO_ISDEREFABLE,
    /** True if operand is a double. */
    TYPEINFO_ISDOUBLE,
    /** True if operand is an enum. */
    TYPEINFO_ISENUM,
    /** True if operand is an exception. */
    TYPEINFO_ISEXCEPTION,
    /** True if operand is a function. */
    TYPEINFO_ISFUNCTION,
    /** True if operand is a hash. */
    TYPEINFO_ISHASH,
    /** True if operand is an integer . */
    TYPEINFO_ISINT,
    /** True if operand is a var/const declared in the local scope. */
    TYPEINFO_ISLOCAL,
    /** True if operand is a native. */
    TYPEINFO_ISNATIVE,
    /** True if operand is in the process of being the 'new' value
        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()).
    */
    TYPEINFO_ISNUMERIC,
    /** True if operand is an Object */
    TYPEINFO_ISOBJECT,
    /** True if operand is a string. */
    TYPEINFO_ISSTRING,
    /** True if operand is a Unique-type value. */
    TYPEINFO_ISUNIQUE,
    /** Evaluates to the type's name (as as the typename
        keyword). */
    TYPEINFO_NAME,
    /** Evaluates to the operand's refcount. */
    TYPEINFO_REFCOUNT
  };
  typedef struct {
    enum TypeInfoWords type;
    char const * word;
    cwal_size_t len;
  } Words;
  static const Words words[] = {
  /* Keep these sorted by their 'word' member. */
  { TYPEINFO_CANNEW,      "cannew", 6},
  { TYPEINFO_HASARRAY,    "hasarray", 8},
  { TYPEINFO_HASENUM,     "hasenum", 7},
  { TYPEINFO_HASEXCEPTION,"hasexception", 12},
  { TYPEINFO_HASHASH,     "hashash", 7},
  { TYPEINFO_HASNATIVE,   "hasnative", 9},
  { TYPEINFO_HASOBJECT,   "hasobject", 9},
  { TYPEINFO_HASPROTOYPE, "hasprototype", 12},
  { TYPEINFO_ISARRAY,     "isarray", 7},
  { TYPEINFO_ISBOOL,      "isbool", 6},
  { TYPEINFO_ISBUFFER,    "isbuffer", 8},
  { TYPEINFO_ISCALLABLE,  "iscallable", 10},
  { TYPEINFO_ISCONTAINER, "iscontainer", 11},
  { TYPEINFO_ISDECLARED,  "isdeclared", 10},
  { TYPEINFO_ISDEREFABLE, "isderefable", 11},
  { TYPEINFO_ISDOUBLE,    "isdouble", 8},
  { TYPEINFO_ISENUM,      "isenum", 6},
  { TYPEINFO_ISEXCEPTION, "isexception", 11},
  { TYPEINFO_ISFUNCTION,  "isfunction", 10},
  { TYPEINFO_ISHASH,      "ishash", 6},
  { TYPEINFO_ISINT,       "isinteger", 9},
  { TYPEINFO_ISLOCAL,     "islocal", 7},
  { TYPEINFO_ISNATIVE,    "isnative", 8},
  { TYPEINFO_ISNEWING,    "isnewing", 8},
  { TYPEINFO_ISNUMBER,    "isnumber", 8},
  { TYPEINFO_ISNUMERIC,   "isnumeric", 9},
  { TYPEINFO_ISOBJECT,    "isobject", 8},
  { TYPEINFO_ISSTRING,    "isstring", 8},
  { TYPEINFO_ISUNIQUE,    "isunique", 8},
  { TYPEINFO_NAME,        "name", 4},
  { TYPEINFO_REFCOUNT,    "refcount", 8},
  { TYPEINFO_NONE, 0, 0}/* end-of-list sentinel: entries must be 0 */
  };
  Words const * word = 0;


  /**
     Ideas:

     typeinfo(FLAG EXPR)

     We use (...) to avoid potential precedence confusion for use
     cases such as:

     typeinfo iscallable X || throw "need a callable type"

     i.e. does the || belong the RHS of the typeinfo or not? We punt
     on that problem by adding the parenthesis, making it unambiguous.

     FLAG is one of the words defined above.
  */
  rc = s2_next_token( se, pr, 0, 0 );
  if(rc) goto end;

  if(S2_T_ParenGroup!=pr->token.ttype){
    rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX,
                       "Expecting (IDENTIFIER EXPR) after '%s'.",
                       kw->word);
    goto end;
  }

  if(se->skipLevel>0){
    /**
       Reminder to self: someday we should arguably continue going
       in skip mode so that we can validate that the contents of (...)
       are syntactically valid.
    */
    *rv = cwal_value_undefined();
    goto end;
  }

  s2_ptoker_sub_from_toker(&prBody, pr);
  rc = s2_next_token(se, &prBody, 0, 0);
  if(rc) goto end;
  else if(S2_T_Identifier!=prBody.token.ttype){
    /*
      TODO? Eval until the comma and check the
      string val of the result?
    */
    rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                       "Expected identifier in %s(IDENTIFIER EXPR).",
                       kw->word);
    goto end;
  }

  se->currentScript = &prBody;
  tIdent = prBody.token;
  assert(tIdent.end-tIdent.begin>0);
  idLen = (cwal_size_t)(tIdent.end-tIdent.begin);
  assert(idLen);
  /* MARKER(("typeinfo tag: %.*s\n", (int)idLen, tIdent.begin)); */

  /*
    Look for sub-keyword match...
    TODO: binary search. Need to move the struct decl
    up a scope for that.
  */
  rc = -1;
  for( word = &words[0]; rc<0 && word->word; ++word ){

    if(idLen == word->len
       && *word->word==*tIdent.begin
       && 0==(rc = cwal_compare_cstr(word->word, word->len, 
                                     tIdent.begin, idLen))){





      break;
    }


  }
  if(0!=rc || !word->word){
    /* Note that in skip mode we've skipped over the whole (...), so
       this won't be triggered in skip mode (an exception to the
       rule/guideline regarding "blatant syntax errors").  Whether or
       not that needs "fixing" (such that this error could be
       triggered in skip mode) is as yet undecided.







>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>
>










<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
|
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<
<

>




















|


















<
<
<
<

|
















<
<
>
|
|
<
|
>
>
>
>
>
|
<
>
>







31954
31955
31956
31957
31958
31959
31960
31961
31962
31963
31964
31965
31966
31967
31968
31969
31970
31971
31972
31973
31974
31975
31976
31977
31978
31979
31980
31981
31982
31983
31984
31985
31986
31987
31988
31989
31990
31991
31992
31993
31994
31995
31996
31997
31998
31999
32000
32001
32002
32003
32004
32005
32006
32007
32008
32009
32010
32011
32012
32013
32014
32015
32016
32017
32018
32019
32020
32021
32022
32023
32024
32025
32026
32027
32028
32029
32030
32031
32032
32033
32034
32035
32036
32037
32038
32039
32040
32041
32042
32043
32044
32045
32046
32047
32048
32049
32050
32051
32052
32053
32054
32055
32056
32057
32058
32059
32060
32061
32062
32063
32064
32065
32066
32067
32068
32069
32070
32071
32072
32073
32074
32075
32076
32077
32078
32079
32080
32081
32082
32083
32084
32085
32086
32087
32088
32089
32090
32091
32092
32093
32094
32095
32096
32097
32098
32099
32100
32101
32102
32103
32104
32105
32106
32107
32108
32109
32110
32111
32112
32113
32114
32115
32116
32117











































































32118






































32119
32120
32121
32122
32123
32124
32125
32126
32127
32128
32129
32130
32131
32132
32133
32134
32135
32136
32137
32138
32139
32140
32141
32142
32143
32144
32145
32146
32147
32148
32149
32150
32151
32152
32153
32154
32155
32156
32157
32158
32159




32160
32161
32162
32163
32164
32165
32166
32167
32168
32169
32170
32171
32172
32173
32174
32175
32176
32177


32178
32179
32180

32181
32182
32183
32184
32185
32186
32187

32188
32189
32190
32191
32192
32193
32194
32195
32196
    cwal_container_client_flags_set(store, S2_VAL_F_ENUM);
    cwal_value_unhand(store);
    *rv = store;
  }
  return rc;
}

/**
   Tag type IDs for typeinfo(TAG ...).
*/
enum s2_typeinfo_words {
  /* Sentinel value. Must be 0. */
  TYPEINFO_NONE = 0,
  /** True if operand can be used as an operand for the "new" keyword. */
  TYPEINFO_CANNEW,
  /** True if operand is or has an array in its prototype chain. */
  TYPEINFO_HASARRAY,
  /** True if operand is or has an enum in its prototype chain. */
  TYPEINFO_HASENUM,
  /** True if operand is or has an exception in its prototype chain. */
  TYPEINFO_HASEXCEPTION,
  /** True if operand is or has a hash in its prototype chain. */
  TYPEINFO_HASHASH,
  /** True if operand is or has a native in its prototype chain. */
  TYPEINFO_HASNATIVE,
  /** True if operand is or has an Object in its prototype chain. */
  TYPEINFO_HASOBJECT,
  /** True if operand has a prototype. */
  TYPEINFO_HASPROTOYPE,
  /** True if operand is an array. */
  TYPEINFO_ISARRAY,
  /** True if operand is a bool. */
  TYPEINFO_ISBOOL,
  /** True if operand is a buffer. */
  TYPEINFO_ISBUFFER,
  /** True if operand is callable (is a function or has a function
      in its prototype chain). */
  TYPEINFO_ISCALLABLE,
  /** True if operand is a container (can hold its own properties). */
  TYPEINFO_ISCONTAINER,
  /** True if operand is the name of a declared (currently in-scope) variable/const. */
  TYPEINFO_ISDECLARED,
  /** True if operand is legal for use with the dot operator. */
  TYPEINFO_ISDEREFABLE,
    /** True if operand is a double. */
  TYPEINFO_ISDOUBLE,
  /** True if operand is an enum. */
  TYPEINFO_ISENUM,
  /** True if operand is an exception. */
  TYPEINFO_ISEXCEPTION,
  /** True if operand is a function. */
  TYPEINFO_ISFUNCTION,
  /** True if operand is a hash. */
  TYPEINFO_ISHASH,
  /** True if operand is an integer . */
  TYPEINFO_ISINT,
  /** 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()).
  */
  TYPEINFO_ISNUMERIC,
  /** True if operand is an Object */
  TYPEINFO_ISOBJECT,
  /** True if operand is a string. */
  TYPEINFO_ISSTRING,
  /** True if operand is a Unique-type value. */
  TYPEINFO_ISUNIQUE,
  /** Evaluates to the type's name (as as the typename
      keyword). */
  TYPEINFO_NAME,
  /** Evaluates to the operand's refcount. */
  TYPEINFO_REFCOUNT
};
typedef struct {
  enum s2_typeinfo_words type;
  char const * word;
  cwal_size_t wordLen;
  /**
     Argument type:

     0 = none
     >0 = expr
     <0 = identifier
  */
  int argType;
} s2_typeinfo_word;
static const s2_typeinfo_word s2TypeInfoWords[] = {
  /* Keep these sorted by their 'word' member, as we
     do a binary search on that. */
  { TYPEINFO_CANNEW,      "cannew", 6,        1},
  { TYPEINFO_HASARRAY,    "hasarray", 8,      1},
  { TYPEINFO_HASENUM,     "hasenum", 7,       1},
  { TYPEINFO_HASEXCEPTION,"hasexception", 12, 1},
  { TYPEINFO_HASHASH,     "hashash", 7,       1},
  { TYPEINFO_HASNATIVE,   "hasnative", 9,     1},
  { TYPEINFO_HASOBJECT,   "hasobject", 9,     1},
  { TYPEINFO_HASPROTOYPE, "hasprototype", 12, 1},
  { TYPEINFO_ISARRAY,     "isarray", 7,       1},
  { TYPEINFO_ISBOOL,      "isbool", 6,        1},
  { TYPEINFO_ISBUFFER,    "isbuffer", 8,      1},
  { TYPEINFO_ISCALLABLE,  "iscallable", 10,   1},
  { TYPEINFO_ISCONTAINER, "iscontainer", 11,  1},
  { TYPEINFO_ISDECLARED,  "isdeclared", 10,  -1},
  { TYPEINFO_ISDEREFABLE, "isderefable", 11,  1},
  { TYPEINFO_ISDOUBLE,    "isdouble", 8,      1},
  { TYPEINFO_ISENUM,      "isenum", 6,        1},
  { TYPEINFO_ISEXCEPTION, "isexception", 11,  1},
  { TYPEINFO_ISFUNCTION,  "isfunction", 10,   1},
  { TYPEINFO_ISHASH,      "ishash", 6,        1},
  { TYPEINFO_ISINT,       "isinteger", 9,     1},
  { TYPEINFO_ISLOCAL,     "islocal", 7,      -1},
  { TYPEINFO_ISNATIVE,    "isnative", 8,      1},
  { TYPEINFO_ISNEWING,    "isnewing", 8,      0},
  { TYPEINFO_ISNUMBER,    "isnumber", 8,      1},
  { TYPEINFO_ISNUMERIC,   "isnumeric", 9,     1},
  { TYPEINFO_ISOBJECT,    "isobject", 8,      1},
  { TYPEINFO_ISSTRING,    "isstring", 8,      1},
  { TYPEINFO_ISUNIQUE,    "isunique", 8,      1},
  { TYPEINFO_NAME,        "name", 4,          1},
  { TYPEINFO_REFCOUNT,    "refcount", 8,      1},
  { TYPEINFO_NONE, 0, 0, 0}/* end-of-list sentinel: entries must be 0 */
};

/**
   Comparison func for bsearch(), comparing (s2_typeinfo_word *)
   key with (s2_typeinfo_word *) on s2_typeinfo_word::word.
*/
static int s2_typeinfo_cmp(void const * key, void const * kw){
  s2_typeinfo_word const * k = ((s2_typeinfo_word const *)key);
  s2_typeinfo_word const * w = ((s2_typeinfo_word const *)kw);
  if(k->wordLen == w->wordLen) return memcmp(k->word, w->word,
                                     (size_t)k->wordLen);
#if !defined(NDEBUG)
  else if(!w->word){
    assert(!"sentinel gets trimmed from the list length");
    return 1 /* sentinel entry */;
  }
#endif
  else if(k->word[0] != w->word[0]) return (int)k->word[0] - (int)w->word[0];
  else{
    cwal_size_t const len = (k->wordLen<w->wordLen) ? k->wordLen : w->wordLen;
    int const cmp = memcmp(k->word, w->word, (size_t)len);
    return cmp ? cmp : ((len==k->wordLen) ? -1 : 1);
  }
}

int s2_keyword_f_typeinfo( s2_keyword const * kw, s2_engine * se,
                           s2_ptoker * pr, cwal_value **rv){
  s2_ptoken tIdent = s2_ptoken_empty;
  s2_ptoker prBody = s2_ptoker_empty;
  s2_ptoker const * oldScript = se->currentScript;
  cwal_size_t idLen = 0;
  cwal_value * xrv = 0;
  int rc;
  int buul = -1 /* <0=unknown, 0=false, >0=true */;











































































  s2_typeinfo_word const * word;







































  *rv = 0;
  /**
     Ideas:

     typeinfo(FLAG EXPR)

     We use (...) to avoid potential precedence confusion for use
     cases such as:

     typeinfo iscallable X || throw "need a callable type"

     i.e. does the || belong the RHS of the typeinfo or not? We punt
     on that problem by adding the parenthesis, making it unambiguous.

     FLAG is one of the words defined above.
  */
  rc = s2_next_token( se, pr, 0, 0 );
  if(rc) goto end;

  if(S2_T_ParenGroup!=pr->token.ttype){
    rc = s2_err_ptoker(se, pr, CWAL_SCR_SYNTAX,
                       "Expecting (IDENTIFIER ...) after '%s'.",
                       kw->word);
    goto end;
  }

  if(se->skipLevel>0){
    /**
       Reminder to self: someday we should arguably continue going
       in skip mode so that we can validate that the contents of (...)
       are syntactically valid.
    */
    *rv = cwal_value_undefined();
    goto end;
  }

  s2_ptoker_sub_from_toker(&prBody, pr);
  rc = s2_next_token(se, &prBody, 0, 0);
  if(rc) goto end;
  else if(S2_T_Identifier!=prBody.token.ttype){




    rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                       "Expected typeinfo tag in %s(TAG ...).",
                       kw->word);
    goto end;
  }

  se->currentScript = &prBody;
  tIdent = prBody.token;
  assert(tIdent.end-tIdent.begin>0);
  idLen = (cwal_size_t)(tIdent.end-tIdent.begin);
  assert(idLen);
  /* MARKER(("typeinfo tag: %.*s\n", (int)idLen, tIdent.begin)); */

  /*
    Look for sub-keyword match...
    TODO: binary search. Need to move the struct decl
    up a scope for that.
  */


  {
    s2_typeinfo_word wordKey;
    wordKey.word = tIdent.begin;

    wordKey.wordLen = idLen;
    wordKey.type = TYPEINFO_REFCOUNT /*any will do*/;
    wordKey.argType = 0;
    word = (s2_typeinfo_word const *)bsearch(&wordKey, s2TypeInfoWords,
                                             sizeof(s2TypeInfoWords)
                                             /sizeof(s2TypeInfoWords[0])
                                             -1/*sentinel*/,

                                             sizeof(s2_typeinfo_word),
                                             s2_typeinfo_cmp);
  }
  if(0!=rc || !word->word){
    /* Note that in skip mode we've skipped over the whole (...), so
       this won't be triggered in skip mode (an exception to the
       rule/guideline regarding "blatant syntax errors").  Whether or
       not that needs "fixing" (such that this error could be
       triggered in skip mode) is as yet undecided.
31770
31771
31772
31773
31774
31775
31776
31777
31778
31779
31780
31781
31782
31783
31784
31785
31786
31787
31788



31789
31790
31791
31792
31793

31794
31795





31796
31797
31798
31799
31800
31801
31802
31803
31804
31805
31806
31807
31808
31809

31810














31811
31812
31813
31814
31815
31816
31817
    rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                       "Expecting comma after %s(%.*s ...).",
                       kw->word, (int)idLen, tIdent.begin);
    goto end;
  }
#endif

  switch(word->type){
    case TYPEINFO_ISLOCAL:
    case TYPEINFO_ISDECLARED:
      rc = s2_next_token(se, &prBody, 0, 0);
      if(rc) goto end;
      else if(S2_T_Identifier != prBody.token.ttype){
        rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                           "The %s('%s' ...) tag requires an IDENTIFIER "
                           "(variable name).",
                           kw->word, word->word);
        goto end;
      }



      xrv = s2_var_get(se, TYPEINFO_ISLOCAL==word->type ? 0 : -1,
                       prBody.token.begin,
                       (cwal_size_t)(prBody.token.end - prBody.token.begin));
      buul = xrv ? 1 : 0;
      xrv = 0;

      break;
    default:





      rc = s2_eval_expr_impl(se, &prBody, 0,
                             (TYPEINFO_NAME==word->type)
                             ? S2_EVAL_UNKNOWN_IDENTIFIER_AS_UNDEFINED
                             : 0
                             , &xrv);
      if(rc){
        xrv = 0;
        goto end;
      }else if(!xrv){
        rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                           "Expecting non-empty expression after %s(%s, ...).",
                           kw->word, word ? word->word : "...");
        goto end;
      }

      break;














  }
  assert(!rc);

  switch(word->type){

#define TYPEWORD(WORD,PREDICATE) \
    case WORD: buul = PREDICATE(xrv); break







|
|
<
|
|
|
|
|
|
|
|
|
>
>
>
|
|
|
|
|
>
|
|
>
>
>
>
>
|
|
|
|
|
|
|
|
|
|
|
|
|
|
>
|
>
>
>
>
>
>
>
>
>
>
>
>
>
>







32212
32213
32214
32215
32216
32217
32218
32219
32220

32221
32222
32223
32224
32225
32226
32227
32228
32229
32230
32231
32232
32233
32234
32235
32236
32237
32238
32239
32240
32241
32242
32243
32244
32245
32246
32247
32248
32249
32250
32251
32252
32253
32254
32255
32256
32257
32258
32259
32260
32261
32262
32263
32264
32265
32266
32267
32268
32269
32270
32271
32272
32273
32274
32275
32276
32277
32278
32279
32280
32281
32282
    rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                       "Expecting comma after %s(%.*s ...).",
                       kw->word, (int)idLen, tIdent.begin);
    goto end;
  }
#endif

  if(word->argType<0){
    /* typeinfo(tag IDENTIFIER) */

    rc = s2_next_token(se, &prBody, 0, 0);
    if(rc) goto end;
    else if(S2_T_Identifier != prBody.token.ttype){
      rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                         "The %s('%s' ...) tag requires an IDENTIFIER "
                         "(variable name).",
                         kw->word, word->word);
      goto end;
    }
    switch(word->type){
      case TYPEINFO_ISLOCAL:
      case TYPEINFO_ISDECLARED:
        xrv = s2_var_get(se, TYPEINFO_ISLOCAL==word->type ? 0 : -1,
                         prBody.token.begin,
                         (cwal_size_t)(prBody.token.end - prBody.token.begin));
        buul = xrv ? 1 : 0;
        xrv = 0;
        cwal_value_ref(xrv);
        break;
      default:
        assert(!"Missing entry for argType<0!");
        break;
    }
  }else if(word->argType>0){
    /* typeinfo(tag EXPR) */
    rc = s2_eval_expr_impl(se, &prBody, 0,
                           (TYPEINFO_NAME==word->type)
                           ? S2_EVAL_UNKNOWN_IDENTIFIER_AS_UNDEFINED
                           : 0
                           , &xrv);
    if(rc){
      assert(!xrv);
      goto end;
    }else if(!xrv){
      rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                         "Expecting non-empty expression after %s(%s, ...).",
                         kw->word, word ? word->word : "...");
      goto end;
    }
    cwal_value_ref(xrv);
  }else{
    /* typeinfo(tag) */
    ++se->skipLevel;
    rc = s2_eval_expr_impl(se, &prBody, 0, 0, &xrv);
    --se->skipLevel;
    if(rc){
      assert(!xrv);
      goto end;
    }else if(xrv){
      cwal_value_ref(xrv);
      rc = s2_err_ptoker(se, &prBody, CWAL_SCR_SYNTAX,
                         "Got unexpected expression in %s(%s <HERE>).",
                         kw->word, word->word);
      goto end;
    }
  }
  assert(!rc);

  switch(word->type){

#define TYPEWORD(WORD,PREDICATE) \
    case WORD: buul = PREDICATE(xrv); break
31837
31838
31839
31840
31841
31842
31843

31844
31845
31846

31847
31848
31849
31850
31851
31852
31853
      break;

    case TYPEINFO_ISLOCAL:
    case TYPEINFO_ISDECLARED:
      /* handled above */
      assert(buul >= 0);
      break;

    case TYPEINFO_ISNEWING:
      buul = s2_value_is_newing(xrv);
      break;

    case TYPEINFO_ISNUMERIC:
      if(!(buul = cwal_value_is_number(xrv))
         && cwal_value_is_string(xrv)){
      /* check for numeric-format strings a-la
         0.prototype.parseNumber(). */
        cwal_size_t slen = 0;
        char const * src = cwal_value_get_cstr(xrv, &slen);







>

|

>







32302
32303
32304
32305
32306
32307
32308
32309
32310
32311
32312
32313
32314
32315
32316
32317
32318
32319
32320
      break;

    case TYPEINFO_ISLOCAL:
    case TYPEINFO_ISDECLARED:
      /* handled above */
      assert(buul >= 0);
      break;

    case TYPEINFO_ISNEWING:
      buul = s2_scope_is_newing(se);
      break;

    case TYPEINFO_ISNUMERIC:
      if(!(buul = cwal_value_is_number(xrv))
         && cwal_value_is_string(xrv)){
      /* check for numeric-format strings a-la
         0.prototype.parseNumber(). */
        cwal_size_t slen = 0;
        char const * src = cwal_value_get_cstr(xrv, &slen);
31946
31947
31948
31949
31950
31951
31952
31953

31954
31955
31956
31957
31958
31959
31960
31961
    *rv = cwal_new_bool(buul ? 1 : 0);
  }else{
    assert(*rv);
  }

  end:
  se->currentScript = oldScript;
  if(xrv && *rv != xrv){

    cwal_refunref(xrv);
  }
  return rc;
}



cwal_value * s2_propagating_get( s2_engine * se ){







|
>
|







32413
32414
32415
32416
32417
32418
32419
32420
32421
32422
32423
32424
32425
32426
32427
32428
32429
    *rv = cwal_new_bool(buul ? 1 : 0);
  }else{
    assert(*rv);
  }

  end:
  se->currentScript = oldScript;
  if(xrv){
    if(!rc && *rv == xrv) cwal_value_unhand(xrv);
    else cwal_value_unref(xrv);
  }
  return rc;
}



cwal_value * s2_propagating_get( s2_engine * se ){
32591
32592
32593
32594
32595
32596
32597
32598
32599
32600
32601
32602
32603
32604
32605
      };
      rc = s2_install_functions(se, proto, funcs, 0 );
      if(rc) goto end;
      else {
        cwal_value * fv = 0;
        s2_get(se, proto, "new", 3, &fv);
        assert(fv && "we JUST put this in there!");
        rc = s2_set_ctor_method( se, proto,
                                 cwal_value_get_function(fv) );
      }
    }

#undef FUNC2
#undef CHECKV
#undef RC







|







33059
33060
33061
33062
33063
33064
33065
33066
33067
33068
33069
33070
33071
33072
33073
      };
      rc = s2_install_functions(se, proto, funcs, 0 );
      if(rc) goto end;
      else {
        cwal_value * fv = 0;
        s2_get(se, proto, "new", 3, &fv);
        assert(fv && "we JUST put this in there!");
        rc = s2_ctor_method_set( se, proto,
                                 cwal_value_get_function(fv) );
      }
    }

#undef FUNC2
#undef CHECKV
#undef RC
34173
34174
34175
34176
34177
34178
34179




34180
34181
34182
34183
34184
34185
34186
#else
#define MARKER(pfexp) (void)0
#endif

#define s2__pop_val s2_engine_pop_value
#define s2__pop_tok s2_engine_pop_token






/**
   Operator for unary and binary addition and subtraction.

   Required stack (from top to bottom):
   Binary: RHS LHS
   Unary: RHS







>
>
>
>







34641
34642
34643
34644
34645
34646
34647
34648
34649
34650
34651
34652
34653
34654
34655
34656
34657
34658
#else
#define MARKER(pfexp) (void)0
#endif

#define s2__pop_val s2_engine_pop_value
#define s2__pop_tok s2_engine_pop_token

/* defined in s2_eval.c */
void s2_dotop_state( s2_engine * se, cwal_value * self,
                     cwal_value * lhs, cwal_value * key );


/**
   Operator for unary and binary addition and subtraction.

   Required stack (from top to bottom):
   Binary: RHS LHS
   Unary: RHS
35245
35246
35247
35248
35249
35250
35251


35252
35253
35254


35255
35256

35257
35258
35259
35260
35261
35262
35263
35264
35265
35266
35267
35268
35269
35270
35271
35272
35273
35274
35275
35276


35277
35278
35279
35280
35281
35282
35283
35284
35285
35286
35287
35288
35289
35290
35291
35292
35293
35294

35295
35296
35297
35298
35299
35300
35301
35302
35303
35304
35305
35306
35307
35308
35309
35310
    rc = s2_engine_err_set(se, CWAL_RC_TYPE,
                           "Invalid LHS value (type %s) for "
                           "'%s' operator.",
                           cwal_value_type_name(lhs),
                           op->sym);
  }else if(se->skipLevel){
    if(S2_T_OpDot == op->id){


      se->dotOpSelf = se->dotOpLhs = lhs;
      se->dotOpKey = rhs;
    }else{


      se->dotOpSelf = 0;
      se->dotOpLhs = se->dotOpKey = cwal_value_undefined();

    }
    *rv = cwal_value_undefined();
    rc = 0;
  }else{ /* X.Y and X#Y... */
    cwal_value * xrv = 0;
    se->dotOpLhs = se->dotOpKey = se->dotOpSelf = 0;
    /* s2_dump_val(lhs,"dot op lhs"); */
    if(S2_T_OpDot == op->id){
      const int isArrayAccess =
        cwal_value_is_integer(rhs)
        && cwal_value_array_part(se->e,lhs)
        /* Workaround to keep array.int's
           array from becoming 'this' if it
           resolves to a function which gets
           called.
        */
        ;
      
      rc = s2_get_v(se, lhs, rhs, &xrv);
      if(!rc && !isArrayAccess) se->dotOpSelf = lhs;


    }else{ /* X#Y */
      cwal_hash * h;
      assert(S2_T_OpHash==op->id);
#if 0
      if(!s2_op_check_overload(op, se, 0, lhs, rhs, &xrv, &rc)
         && !rc){
#endif
        h = cwal_value_hash_part(se->e, lhs);
        if(!h){
          rc = s2_engine_err_set(se, CWAL_RC_TYPE,
                                 "Invalid (non-Hash) LHS for '%s' operator.",
                                 op->sym);
        }else{
          xrv = cwal_hash_search_v( h, rhs );
        }
#if 0
      }
#endif

    }
    if(!rc){
      /*
        Hmmm. We can't cwal_refunref() the operands to these
        ops. We need to tweak the bits which use these parts
        (namely assignments) to refunref these.
       */
      se->dotOpLhs = lhs;
      se->dotOpKey = rhs;
      *rv = xrv ? xrv : cwal_value_undefined();
    }
  }
  return rc;
}

int s2_op_f_oload_arrow( s2_op const * op, s2_engine * se,







>
>
|
|

>
>
|
|
>





|













|
>
>


















>







|
<







35717
35718
35719
35720
35721
35722
35723
35724
35725
35726
35727
35728
35729
35730
35731
35732
35733
35734
35735
35736
35737
35738
35739
35740
35741
35742
35743
35744
35745
35746
35747
35748
35749
35750
35751
35752
35753
35754
35755
35756
35757
35758
35759
35760
35761
35762
35763
35764
35765
35766
35767
35768
35769
35770
35771
35772
35773
35774
35775
35776
35777
35778
35779
35780
35781
35782

35783
35784
35785
35786
35787
35788
35789
    rc = s2_engine_err_set(se, CWAL_RC_TYPE,
                           "Invalid LHS value (type %s) for "
                           "'%s' operator.",
                           cwal_value_type_name(lhs),
                           op->sym);
  }else if(se->skipLevel){
    if(S2_T_OpDot == op->id){
      s2_dotop_state( se, lhs, lhs, rhs );
      assert( se->dotOpSelf == se->dotOpLhs );
      assert( se->dotOpSelf == lhs );
      assert( se->dotOpKey == rhs );
    }else{
      s2_dotop_state( se, 0, cwal_value_undefined(),
                      cwal_value_undefined() );
      assert(!se->dotOpSelf);
      assert(cwal_value_undefined() == se->dotOpLhs);
      assert(cwal_value_undefined() == se->dotOpKey);
    }
    *rv = cwal_value_undefined();
    rc = 0;
  }else{ /* X.Y and X#Y... */
    cwal_value * xrv = 0;
    /* s2_dotop_state( se, 0, 0, 0 ); */
    /* s2_dump_val(lhs,"dot op lhs"); */
    if(S2_T_OpDot == op->id){
      const int isArrayAccess =
        cwal_value_is_integer(rhs)
        && cwal_value_array_part(se->e,lhs)
        /* Workaround to keep array.int's
           array from becoming 'this' if it
           resolves to a function which gets
           called.
        */
        ;
      
      rc = s2_get_v(se, lhs, rhs, &xrv);
      if(!rc){
        s2_dotop_state( se, isArrayAccess ? 0 : lhs, lhs, rhs );
      }
    }else{ /* X#Y */
      cwal_hash * h;
      assert(S2_T_OpHash==op->id);
#if 0
      if(!s2_op_check_overload(op, se, 0, lhs, rhs, &xrv, &rc)
         && !rc){
#endif
        h = cwal_value_hash_part(se->e, lhs);
        if(!h){
          rc = s2_engine_err_set(se, CWAL_RC_TYPE,
                                 "Invalid (non-Hash) LHS for '%s' operator.",
                                 op->sym);
        }else{
          xrv = cwal_hash_search_v( h, rhs );
        }
#if 0
      }
#endif
      if(!rc) s2_dotop_state(se, 0, lhs, rhs);
    }
    if(!rc){
      /*
        Hmmm. We can't cwal_refunref() the operands to these
        ops. We need to tweak the bits which use these parts
        (namely assignments) to refunref these.
       */
      /* s2_dotop_state( se, opSelf, lhs, rhs ); */

      *rv = xrv ? xrv : cwal_value_undefined();
    }
  }
  return rc;
}

int s2_op_f_oload_arrow( s2_op const * op, s2_engine * se,
35322
35323
35324
35325
35326
35327
35328
35329
35330
35331
35332
35333
35334
35335
35336
35337
      assert(rc);
    }
#if 0
    /*??? can of worms ??? */
    if(!rc){
      /* Has to be done afterwards b/c overload can overwrite these,
         triggering an assertion in fcall() */
      se->dotOpKey = rhs;
      se->dotOpLhs = lhs /* *rv */;
      /* s2_dump_val(rhs,"se->dotOpKey"); */
      /* s2_dump_val(lhs,"se->dotOpLhs"); */
    }
#endif
  }
  return rc;
}







|
<







35801
35802
35803
35804
35805
35806
35807
35808

35809
35810
35811
35812
35813
35814
35815
      assert(rc);
    }
#if 0
    /*??? can of worms ??? */
    if(!rc){
      /* Has to be done afterwards b/c overload can overwrite these,
         triggering an assertion in fcall() */
      s2_dotop_state( se, se->dotOpSelf, lhs, rhs );

      /* s2_dump_val(rhs,"se->dotOpKey"); */
      /* s2_dump_val(lhs,"se->dotOpLhs"); */
    }
#endif
  }
  return rc;
}
35566
35567
35568
35569
35570
35571
35572


35573


35574
35575
35576
35577
35578
35579
35580
35581
35582


35583

35584
35585
35586
35587
35588
35589
35590
  cwal_value * vResolved = 0, * vResult = 0;
  cwal_int_t addVal = 0;
  char gotOverLoad = 0;
  char resolvedIsContainer = 0;
  assert(1==op->arity);
  self = se->dotOpLhs;
  key = self ? se->dotOpKey : 0;


  se->dotOpLhs = se->dotOpKey = 0;


  se->dotOpId = 0;
  vTok = s2__pop_tok(se, 1);
  if(se->skipLevel>0){
    /* FIXME: refunref self/key, as appropriate.
       What about dotOpId?*/
    *rv = cwal_value_undefined();
    s2_stoken_free(se, vTok, 1);
    return 0;
  }


  if(!self) key = vTok->value;

  identType = vTok->ttype;
  s2_stoken_free(se, vTok, 1);
  vTok = 0;

  /*
    if (self) then we know (well, hope) this was a prefix op to
    X.Y.







>
>
|
>
>









>
>
|
>







36044
36045
36046
36047
36048
36049
36050
36051
36052
36053
36054
36055
36056
36057
36058
36059
36060
36061
36062
36063
36064
36065
36066
36067
36068
36069
36070
36071
36072
36073
36074
36075
  cwal_value * vResolved = 0, * vResult = 0;
  cwal_int_t addVal = 0;
  char gotOverLoad = 0;
  char resolvedIsContainer = 0;
  assert(1==op->arity);
  self = se->dotOpLhs;
  key = self ? se->dotOpKey : 0;
  if(key) cwal_value_ref(key);
  if(self) cwal_value_ref(self);
  s2_dotop_state( se, 0, 0, 0 );
  if(key) cwal_value_unhand(key);
  if(self) cwal_value_unhand(self);
  se->dotOpId = 0;
  vTok = s2__pop_tok(se, 1);
  if(se->skipLevel>0){
    /* FIXME: refunref self/key, as appropriate.
       What about dotOpId?*/
    *rv = cwal_value_undefined();
    s2_stoken_free(se, vTok, 1);
    return 0;
  }
  if(!self){
    assert(!key);
    key = vTok->value;
  }
  identType = vTok->ttype;
  s2_stoken_free(se, vTok, 1);
  vTok = 0;

  /*
    if (self) then we know (well, hope) this was a prefix op to
    X.Y.
36637
36638
36639
36640
36641
36642
36643
36644
36645
36646
36647
36648
36649
36650
36651
    };
    rc = s2_install_functions(se, proto, funcs, 0);
    if(rc) goto end;
    else {
      cwal_value * fv = 0;
      s2_get(se, proto, "new", 3, &fv);
      assert(fv && "we JUST put this in there!");
      rc = s2_set_ctor_method( se, proto,
                               cwal_value_get_function(fv) );
    }
  }

#undef SET
#undef CHECKV
    end:







|







37122
37123
37124
37125
37126
37127
37128
37129
37130
37131
37132
37133
37134
37135
37136
    };
    rc = s2_install_functions(se, proto, funcs, 0);
    if(rc) goto end;
    else {
      cwal_value * fv = 0;
      s2_get(se, proto, "new", 3, &fv);
      assert(fv && "we JUST put this in there!");
      rc = s2_ctor_method_set( se, proto,
                               cwal_value_get_function(fv) );
    }
  }

#undef SET
#undef CHECKV
    end:
38502
38503
38504
38505
38506
38507
38508
38509
38510
38511
38512
38513
38514
38515
38516
      };
      rc = s2_install_functions(se, proto, funcs, 0);
      if(rc) goto end;
      else {
        cwal_value * fv = 0;
        s2_get(se, proto, "new", 3, &fv);
        assert(fv && "we JUST put this in there!");
        rc = s2_set_ctor_method( se, proto,
                                 cwal_value_get_function(fv) );
      }
    }

    end:
    return rc
      ? NULL /* remember: proto is stashed at this point, so no leak. */







|







38987
38988
38989
38990
38991
38992
38993
38994
38995
38996
38997
38998
38999
39000
39001
      };
      rc = s2_install_functions(se, proto, funcs, 0);
      if(rc) goto end;
      else {
        cwal_value * fv = 0;
        s2_get(se, proto, "new", 3, &fv);
        assert(fv && "we JUST put this in there!");
        rc = s2_ctor_method_set( se, proto,
                                 cwal_value_get_function(fv) );
      }
    }

    end:
    return rc
      ? NULL /* remember: proto is stashed at this point, so no leak. */
Changes to s2/s2_amalgamation.h.
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#  endif
#  ifndef _BSD_SOURCE
#    define _BSD_SOURCE
#  endif
#endif
/* start of file ../cwal_amalgamation.h */
#if !defined(CWAL_VERSION_STRING)
#  define CWAL_VERSION_STRING "cwal 978e458f42aa375da98a4ed1ee34919cd59c7e26 2016-01-26 16:57:35 built 2016-01-26 17:03"
#endif
/* start of file include/wh/cwal/cwal_config.h */
#if !defined(WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED)
#define WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED 1

#if !defined(CWAL_VERSION_STRING)
#define CWAL_VERSION_STRING "cwal version ???"







|







11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#  endif
#  ifndef _BSD_SOURCE
#    define _BSD_SOURCE
#  endif
#endif
/* start of file ../cwal_amalgamation.h */
#if !defined(CWAL_VERSION_STRING)
#  define CWAL_VERSION_STRING "cwal fc4ced93115269518c0b2f51baafc3bf6d718e95 2016-01-31 17:26:10 built 2016-01-31 17:33"
#endif
/* start of file include/wh/cwal/cwal_config.h */
#if !defined(WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED)
#define WANDERINGHORSE_NET_CWAL_CONFIG_H_INCLUDED 1

#if !defined(CWAL_VERSION_STRING)
#define CWAL_VERSION_STRING "cwal version ???"
8445
8446
8447
8448
8449
8450
8451



8452
8453
8454
8455
8456
8457
8458
   Returns a handle to v's originating cwal_engine, or NULL
   if !v.
*/
cwal_engine * cwal_value_engine( cwal_value const * v );

/**
   Returns the current owning scope of v, or NULL if !v.



*/
cwal_scope * cwal_value_scope( cwal_value const * v );


/**
   Possibly reallocates self->list, changing its size. This
   function ensures that self->list has at least n entries. If n







>
>
>







8445
8446
8447
8448
8449
8450
8451
8452
8453
8454
8455
8456
8457
8458
8459
8460
8461
   Returns a handle to v's originating cwal_engine, or NULL
   if !v.
*/
cwal_engine * cwal_value_engine( cwal_value const * v );

/**
   Returns the current owning scope of v, or NULL if !v.

   Note that this is always 0 for values for which
   cwal_value_is_builtin() returns true.
*/
cwal_scope * cwal_value_scope( cwal_value const * v );


/**
   Possibly reallocates self->list, changing its size. This
   function ensures that self->list has at least n entries. If n
10683
10684
10685
10686
10687
10688
10689

10690
10691
10692
10693
10694
10695
10696
   O(1) and cost no new memory.

   Token instances must not be in use more than once concurrently,
   e.g. a token may not be in more than one stack at a time, nor may
   it be in the same stack multiple times. Multiple entries may
   reference the same value, provided it is otherwise safe from being
   swept/vacuumed up.

*/
struct s2_stoken{
  /**
     A s2_token_types value.
  */
  int ttype;
  /**







>







10686
10687
10688
10689
10690
10691
10692
10693
10694
10695
10696
10697
10698
10699
10700
   O(1) and cost no new memory.

   Token instances must not be in use more than once concurrently,
   e.g. a token may not be in more than one stack at a time, nor may
   it be in the same stack multiple times. Multiple entries may
   reference the same value, provided it is otherwise safe from being
   swept/vacuumed up.

*/
struct s2_stoken{
  /**
     A s2_token_types value.
  */
  int ttype;
  /**
10899
10900
10901
10902
10903
10904
10905













10906
10907
10908
10909
10910
10911
10912
   copy initialization.
*/
extern const s2_op s2_op_empty;
#endif

/**
   Holds a stack of s2_stokens.













*/
struct s2_stoken_stack {
  /**
     The top of the stack.

     Maintained via s2_stoken_stack_push(),
     s2_stoken_stack_pop(), and friends.







>
>
>
>
>
>
>
>
>
>
>
>
>







10903
10904
10905
10906
10907
10908
10909
10910
10911
10912
10913
10914
10915
10916
10917
10918
10919
10920
10921
10922
10923
10924
10925
10926
10927
10928
10929
   copy initialization.
*/
extern const s2_op s2_op_empty;
#endif

/**
   Holds a stack of s2_stokens.


   Reminders to self:

   - 20160131 this type was designed to _not_ ref/unref values added
   to the stack, but that may have turned out to be a mistake. It
   simplifies much other code but string interning combined with our
   internal use of "temp" values leads to unmanagable/incorrect
   refcounts for interned string values in some cases, triggering
   cwal-level corruption detection assertions. Rewiring s2 so that
   this type does much of the reference count managent for a given
   (sub)expression is certainly worth considering, but could require a
   weekend or more of intense concentration :/.
*/
struct s2_stoken_stack {
  /**
     The top of the stack.

     Maintained via s2_stoken_stack_push(),
     s2_stoken_stack_pop(), and friends.
11074
11075
11076
11077
11078
11079
11080






11081
11082
11083
11084
11085
11086
11087
11088

11089
11090
11091
11092
11093
11094
11095
 */
struct s2_scope {
  cwal_scope scope;
  s2_sweep_guard sguard;
  s2_scope * parent;
  s2_subexpr_savestate saved;
  int ctorCallFlag;






};

#define s2_scope_empty_m {\
    cwal_scope_empty_m/*scope*/, \
    s2_sweep_guard_empty_m/*sguard*/,         \
    0/*parent*/, \
    s2_subexpr_savestate_empty_m/*saved*/,    \
    0/*ctorCallFlag*/\

}
extern const s2_scope s2_scope_empty;

/**
   This class encapsulates a basic stack engine which uses
   the cwal_value type as its generic operand type.








>
>
>
>
>
>







|
>







11091
11092
11093
11094
11095
11096
11097
11098
11099
11100
11101
11102
11103
11104
11105
11106
11107
11108
11109
11110
11111
11112
11113
11114
11115
11116
11117
11118
11119
 */
struct s2_scope {
  cwal_scope scope;
  s2_sweep_guard sguard;
  s2_scope * parent;
  s2_subexpr_savestate saved;
  int ctorCallFlag;
  /**
     Set very to allow typeinfo(issnewing) to elide its second
     argument. It also incidentally lets us implement that
     check more cleanly.
  */
  void /*cwal_value*/ const * newingThis;
};

#define s2_scope_empty_m {\
    cwal_scope_empty_m/*scope*/, \
    s2_sweep_guard_empty_m/*sguard*/,         \
    0/*parent*/, \
    s2_subexpr_savestate_empty_m/*saved*/,    \
    0/*ctorCallFlag*/,                      \
    0/*newingThis*/\
}
extern const s2_scope s2_scope_empty;

/**
   This class encapsulates a basic stack engine which uses
   the cwal_value type as its generic operand type.

11292
11293
11294
11295
11296
11297
11298
11299
11300
11301
11302
11303
11304
11305
11306

  /**
     Used to communicate the "argv" value between the interpreter
     parts which call functions and the pre-call hook called by
     cwal.
  */
  cwal_array * callArgV;
  
  /**
     Gets set by the stack layer when an operator (A) triggers an
     error and (B) has its srcPos set. Used to communicate
     operator-triggered error location information back to the parser
     layer.
  */
  char const * opErrPos;







|







11316
11317
11318
11319
11320
11321
11322
11323
11324
11325
11326
11327
11328
11329
11330

  /**
     Used to communicate the "argv" value between the interpreter
     parts which call functions and the pre-call hook called by
     cwal.
  */
  cwal_array * callArgV;

  /**
     Gets set by the stack layer when an operator (A) triggers an
     error and (B) has its srcPos set. Used to communicate
     operator-triggered error location information back to the parser
     layer.
  */
  char const * opErrPos;
15018
15019
15020
15021
15022
15023
15024
15025
15026
15027
15028
15029
15030
15031
15032
15033
15034
15035
15036
15037
15038
15039
15040
15041
15042
15043
15044
15045
15046
15047
15048
15049
15050
15051
15052
15053
   cwal_value * fv;
   f = cwal_new_function( se->e, my_callback_f, my_callback_state,
                          my_callback_state_finalizer_f, my_type_id );
   if(!f) return CWAL_RC_OOM;
   fv = cwal_function_value(fv);
   assert(f == cwal_value_get_function(fv)); // will always be the case if f is valid
   cwal_value_ref(fv);
   rc = s2_set_ctor_method( se, myContainer, f );
   cwal_value_unref(fv);
   return rc;
   @endcode

   In such a setup, from inside the my_callback() implementation,
   cwal_args_state(args->engine, my_type_id) can be used to
   (type-safely) fetch the my_callback_state pointer. The s2_engine
   instance associated with the call can be fetched via
   s2_engine_from_args(args).

   @see s2_set_ctor_callback()
*/
int s2_set_ctor_method( s2_engine * se, cwal_value * container, cwal_function * method );

/**
   A convenience form of s2_set_ctor_method() which instantiates a
   cwal_function from the its 3rd argument using s2_new_callback().
   Returns CWAL_RC_MISUSE if any argument is NULL, else returns as for
   s2_set_ctor_method().
*/
int s2_set_ctor_callback( s2_engine * se, cwal_value * container, cwal_callback_f method );

/**
   Invokes a "constructor" function in the same manner as the 'new' keyword
   does. If ctor is NULL then s2 looks in operand (which must be a container
   type) for a property named "s2::new". If that property is not found or
   is not a Function, se's error state is set and non-0 is returned.








|










|

|


|


|

|







15042
15043
15044
15045
15046
15047
15048
15049
15050
15051
15052
15053
15054
15055
15056
15057
15058
15059
15060
15061
15062
15063
15064
15065
15066
15067
15068
15069
15070
15071
15072
15073
15074
15075
15076
15077
   cwal_value * fv;
   f = cwal_new_function( se->e, my_callback_f, my_callback_state,
                          my_callback_state_finalizer_f, my_type_id );
   if(!f) return CWAL_RC_OOM;
   fv = cwal_function_value(fv);
   assert(f == cwal_value_get_function(fv)); // will always be the case if f is valid
   cwal_value_ref(fv);
   rc = s2_ctor_method_set( se, myContainer, f );
   cwal_value_unref(fv);
   return rc;
   @endcode

   In such a setup, from inside the my_callback() implementation,
   cwal_args_state(args->engine, my_type_id) can be used to
   (type-safely) fetch the my_callback_state pointer. The s2_engine
   instance associated with the call can be fetched via
   s2_engine_from_args(args).

   @see s2_ctor_callback_set()
*/
int s2_ctor_method_set( s2_engine * se, cwal_value * container, cwal_function * method );

/**
   A convenience form of s2_ctor_method_set() which instantiates a
   cwal_function from the its 3rd argument using s2_new_callback().
   Returns CWAL_RC_MISUSE if any argument is NULL, else returns as for
   s2_ctor_method_set().
*/
int s2_ctor_callback_set( s2_engine * se, cwal_value * container, cwal_callback_f method );

/**
   Invokes a "constructor" function in the same manner as the 'new' keyword
   does. If ctor is NULL then s2 looks in operand (which must be a container
   type) for a property named "s2::new". If that property is not found or
   is not a Function, se's error state is set and non-0 is returned.

15093
15094
15095
15096
15097
15098
15099
15100
15101
15102
15103
15104
15105
15106
15107
15108
15109
15110
15111
15112
   "new" keyword. This only returns true if v is a container which
   holds (not counting properties inherited from prototypes) a key
   named "s2::new" with a value which is-a/has-a Function.
*/
char s2_value_is_newable( s2_engine * se, cwal_value * v );

/**
   Returns true (non-0) if argument was created in the context of an
   s2 "new" keyword constructor call and has not yet completed the
   new'ing prcess, else false (0).  Intended only to be called from
   cwal_callback_f() implementations and only to be passed args->self.
*/
char s2_value_is_newing( cwal_value const * thisForCurrentCall );

/**
   @internal

   Looks for a constructor function from operand, _not_ looking in
   prototypes (because ctors should generally not be inherited).








|
|
<
<

|







15117
15118
15119
15120
15121
15122
15123
15124
15125


15126
15127
15128
15129
15130
15131
15132
15133
15134
   "new" keyword. This only returns true if v is a container which
   holds (not counting properties inherited from prototypes) a key
   named "s2::new" with a value which is-a/has-a Function.
*/
char s2_value_is_newable( s2_engine * se, cwal_value * v );

/**
   Returns true (non-0) if se's current scope was started by the "new"
   keyword.


*/
char s2_scope_is_newing(s2_engine * se);

/**
   @internal

   Looks for a constructor function from operand, _not_ looking in
   prototypes (because ctors should generally not be inherited).

Changes to s2/shell.c.
113
114
115
116
117
118
119



120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
#else
static void noop_printf(char const * fmt, ...) {}
#define MARKER if(0) noop_printf
#endif
#define MESSAGE(pfexp) printf pfexp
#define VERBOSE(pfexp) if(App.verboseMode) { printf("verbose: "); MESSAGE(pfexp); }(void)0




/*
  If S2_SHELL_EXTEND is defined, the client must define
  s2_shell_extend() (see shell_extend.c for one implementation) and
  link it in with this app. It will be called relatively early in the
  initialization process so that any auto-loaded scripts can make use
  of it. It must, on error, return one of the non-0 CWAL_RC_xxx error
  codes (NOT a code from outside the range! EVER!). An error is
  treated as fatal to the shell.

  See shell_extend.c for a documented example of how to use this
  approach to extending this app.

*/
#if defined(S2_SHELL_EXTEND)
extern int s2_shell_extend(s2_engine * se, int argc, char const * const * argv);
#endif

/**
   Global app-level state.
*/
static struct {
  char const * appName;







>
>
>














|







113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
#else
static void noop_printf(char const * fmt, ...) {}
#define MARKER if(0) noop_printf
#endif
#define MESSAGE(pfexp) printf pfexp
#define VERBOSE(pfexp) if(App.verboseMode) { printf("verbose: "); MESSAGE(pfexp); }(void)0

#if !defined(S2_SHELL_EXTEND_FUNC_NAME)
#define S2_SHELL_EXTEND_FUNC_NAME s2_shell_extend
#endif
/*
  If S2_SHELL_EXTEND is defined, the client must define
  s2_shell_extend() (see shell_extend.c for one implementation) and
  link it in with this app. It will be called relatively early in the
  initialization process so that any auto-loaded scripts can make use
  of it. It must, on error, return one of the non-0 CWAL_RC_xxx error
  codes (NOT a code from outside the range! EVER!). An error is
  treated as fatal to the shell.

  See shell_extend.c for a documented example of how to use this
  approach to extending this app.

*/
#if defined(S2_SHELL_EXTEND)
extern int S2_SHELL_EXTEND_FUNC_NAME(s2_engine * se, int argc, char const * const * argv);
#endif

/**
   Global app-level state.
*/
static struct {
  char const * appName;
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
  */
  cwal_memcap_config memcap;
} App = {
0/*appName*/,
1/*enableArg0Autoload*/,
1/*enableValueRecycling*/,
35/*maxChunkCount*/,
0/*enableStringInterning*/,
0/*showMetrics*/,
0/*verboseMode*/,
0/*traceAssertions*/,
0/*traceStacks*/,
0/*traceSweeps*/,
1/*scopesUseHashes*/,
0/*inFile*/,







|







204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
  */
  cwal_memcap_config memcap;
} App = {
0/*appName*/,
1/*enableArg0Autoload*/,
1/*enableValueRecycling*/,
35/*maxChunkCount*/,
1/*enableStringInterning*/,
0/*showMetrics*/,
0/*verboseMode*/,
0/*traceAssertions*/,
0/*traceStacks*/,
0/*traceSweeps*/,
1/*scopesUseHashes*/,
0/*inFile*/,
1034
1035
1036
1037
1038
1039
1040



1041
1042
1043
1044
1045
1046
1047
1048




1049
1050
1051
1052
1053
1054
1055
    slen = cwal_strlen(arg0) - (exe ? 4 : 0);
    rc = sprintf(fn, "%.*s.s2", (int)slen, arg0);
    assert(rc<BufSize-1);
    rc = 0;
    scriptName = fn;
  }
  if(s2_file_is_accessible(scriptName, 0)){



    cwal_value * rv = 0;
    VERBOSE(("Auto-loading script [%s].\n", scriptName));
    rc = s2_eval_filename(se, 1, scriptName, -1, &rv);
    if(rc){
      MESSAGE(("Error auto-loading init script [%s]: "
               "error #%d (%s).\n", scriptName,
               rc, s2_rc_cstr(rc)));
      s2sh_report_result(se, rc, &rv);




    }
  }
  return rc;
}

int s2sh_main(int argc, char const * const * argv){
  enum {







>
>
>
|
|
|
|
|
|
|
|
>
>
>
>







1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
    slen = cwal_strlen(arg0) - (exe ? 4 : 0);
    rc = sprintf(fn, "%.*s.s2", (int)slen, arg0);
    assert(rc<BufSize-1);
    rc = 0;
    scriptName = fn;
  }
  if(s2_file_is_accessible(scriptName, 0)){
    if(App.cleanroom){
      VERBOSE(("Clean-room mode is active: NOT auto-loading script [%s].\n", scriptName));
    }else{
      cwal_value * rv = 0;
      VERBOSE(("Auto-loading script [%s].\n", scriptName));
      rc = s2_eval_filename(se, 1, scriptName, -1, &rv);
      if(rc){
        MESSAGE(("Error auto-loading init script [%s]: "
                 "error #%d (%s).\n", scriptName,
                 rc, s2_rc_cstr(rc)));
        s2sh_report_result(se, rc, &rv);
      }
      else{
        cwal_refunref(rv);
      }
    }
  }
  return rc;
}

int s2sh_main(int argc, char const * const * argv){
  enum {
1101
1102
1103
1104
1105
1106
1107

1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121

1122



1123
1124
1125
1126
1127
1128
1129
  se = UseStackS2Engine ? &SE : s2_engine_alloc(e);
  assert(se);
  assert(UseStackS2Engine ? !se->allocStamp : (se->allocStamp == e));

  if((rc = s2_engine_init( se, e ))) goto end;
  if(!App.cleanroom){
    if((rc = s2_install_core_prototypes(se))) goto end;

  }
  se->sweepInterval += App.addSweepInterval;
  se->vacuumInterval += App.addVacuumInterval;
  se->flags.traceStack = App.traceStacks;
  se->flags.traceAssertions = App.traceAssertions;
  se->flags.traceSweeps = App.traceSweeps;
  s2_clampdown_level_set(se, App.clampDown);

  if( (rc = s2_setup_s2_global(se)) ) goto end;
  s2_set_interrupt_handlable( se )
    /* after infrastructure, before the auto-loaded script, in case it
       contains an endless loop or some such.
    */;
#if defined(S2_SHELL_EXTEND)

  if( (rc = s2_shell_extend(se, argc, argv)) ) goto end;



#endif
  if( (rc=s2sh_autoload(se)) ) goto end;

  rc = s2sh_main2(se);
  App.s2Global = 0;

  end:







>








<





>
|
>
>
>







1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126

1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
  se = UseStackS2Engine ? &SE : s2_engine_alloc(e);
  assert(se);
  assert(UseStackS2Engine ? !se->allocStamp : (se->allocStamp == e));

  if((rc = s2_engine_init( se, e ))) goto end;
  if(!App.cleanroom){
    if((rc = s2_install_core_prototypes(se))) goto end;
    else if( (rc = s2_setup_s2_global(se)) ) goto end;
  }
  se->sweepInterval += App.addSweepInterval;
  se->vacuumInterval += App.addVacuumInterval;
  se->flags.traceStack = App.traceStacks;
  se->flags.traceAssertions = App.traceAssertions;
  se->flags.traceSweeps = App.traceSweeps;
  s2_clampdown_level_set(se, App.clampDown);


  s2_set_interrupt_handlable( se )
    /* after infrastructure, before the auto-loaded script, in case it
       contains an endless loop or some such.
    */;
#if defined(S2_SHELL_EXTEND)
  if(!App.cleanroom){
    if( (rc = S2_SHELL_EXTEND_FUNC_NAME(se, argc, argv)) ) goto end;
  }else{
    VERBOSE(("Clean-room mode is active: NOT running client-side shell extensions.\n"));
  }
#endif
  if( (rc=s2sh_autoload(se)) ) goto end;

  rc = s2sh_main2(se);
  App.s2Global = 0;

  end:
1423
1424
1425
1426
1427
1428
1429

1430
1431
1432


1433

1434
1435
1436
1437
1438
1439
1440
    }ARG("--W"){
      App.traceSweeps = 0;
    }ARG("-w"){
      ++App.addSweepInterval;
    }ARG("+w"){
      ++App.addVacuumInterval;
    }ARG("-S"){

      MARKER(("WARNING: string interning is disabled (despite -S) "
              "because it can backfire badly in some "
              "(unpredictable) cases.\n"));


      /* App.enableStringInterning = 1; */

    }ARG("--S"){
      App.enableStringInterning = 0;
    }ARG("-i"){
      App.interactive = 1;
    }ARG("-f"){
      GET_FLAG_VAL(App.inFile);
      assert(App.inFile);







>



>
>
|
>







1437
1438
1439
1440
1441
1442
1443
1444
1445
1446
1447
1448
1449
1450
1451
1452
1453
1454
1455
1456
1457
1458
    }ARG("--W"){
      App.traceSweeps = 0;
    }ARG("-w"){
      ++App.addSweepInterval;
    }ARG("+w"){
      ++App.addVacuumInterval;
    }ARG("-S"){
#if 0
      MARKER(("WARNING: string interning is disabled (despite -S) "
              "because it can backfire badly in some "
              "(unpredictable) cases.\n"));
      App.enableStringInterning = 0;
#else
      App.enableStringInterning = 1;
#endif
    }ARG("--S"){
      App.enableStringInterning = 0;
    }ARG("-i"){
      App.interactive = 1;
    }ARG("-f"){
      GET_FLAG_VAL(App.inFile);
      assert(App.inFile);
1487
1488
1489
1490
1491
1492
1493

1494
1495
1496
1497
1498
1499
1500
#define SO(T) MARKER(("sizeof("#T")=%d\n", (int)sizeof(T)))
      SO(s2_stoken);
      SO(s2_ptoken);
      SO(s2_ptoker);
      SO(s2_op);
      SO(s2_engine);
      SO(cwal_engine);

#undef SO
    }else{
      MARKER(("Unknown argument: %s\n", arg));
      assert(!"Unknown argument");
      return CWAL_RC_MISUSE;
    }
  }







>







1505
1506
1507
1508
1509
1510
1511
1512
1513
1514
1515
1516
1517
1518
1519
#define SO(T) MARKER(("sizeof("#T")=%d\n", (int)sizeof(T)))
      SO(s2_stoken);
      SO(s2_ptoken);
      SO(s2_ptoker);
      SO(s2_op);
      SO(s2_engine);
      SO(cwal_engine);
      SO(cwal_memchunk_overlay);
#undef SO
    }else{
      MARKER(("Unknown argument: %s\n", arg));
      assert(!"Unknown argument");
      return CWAL_RC_MISUSE;
    }
  }
Changes to s2/shell_extend.c.
2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
  FUNC("transactionState", cb_fsl_db_trans_state);
  FUNC("close", cb_fsl_db_finalize);
  FUNC("each", cb_fsl_db_each);
  FUNC("exec", cb_fsl_db_exec);
  FUNC("execMulti", cb_fsl_db_exec_multi);
  FUNC("lastInsertId", cb_fsl_db_last_insert_id);
  FUNC("open", cb_fsl_db_ctor);
  rc = s2_set_ctor_method(se, proto, cwal_value_get_function(v));
  if(rc) goto end;
  FUNC("prepare", cb_fsl_db_prepare);
  FUNC("selectValue", cb_fsl_db_select_value);
  FUNC("selectValues", cb_fsl_db_select_values);

  v = fsl_stmt_prototype(se);
  VCHECK;







|







2696
2697
2698
2699
2700
2701
2702
2703
2704
2705
2706
2707
2708
2709
2710
  FUNC("transactionState", cb_fsl_db_trans_state);
  FUNC("close", cb_fsl_db_finalize);
  FUNC("each", cb_fsl_db_each);
  FUNC("exec", cb_fsl_db_exec);
  FUNC("execMulti", cb_fsl_db_exec_multi);
  FUNC("lastInsertId", cb_fsl_db_last_insert_id);
  FUNC("open", cb_fsl_db_ctor);
  rc = s2_ctor_method_set(se, proto, cwal_value_get_function(v));
  if(rc) goto end;
  FUNC("prepare", cb_fsl_db_prepare);
  FUNC("selectValue", cb_fsl_db_select_value);
  FUNC("selectValues", cb_fsl_db_select_values);

  v = fsl_stmt_prototype(se);
  VCHECK;
2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
  /* FUNC("closeConfig", cb_fsl_config_close); */
  FUNC("finalize", cb_fsl_cx_finalize);
  FUNC("getUserName", cb_fsl_cx_user_name);
  FUNC("loadBlob", cb_fsl_cx_content_get);
  FUNC("loadManifest",cb_fsl_cx_deck_load);
  FUNC("loginCookieName", cb_fsl_login_cookie_name);
  FUNC("new", cb_fsl_cx_ctor);
  rc = s2_set_ctor_method(se, proto, cwal_value_get_function(v));
  if(rc) goto end;
  FUNC("openCheckout", cb_fsl_checkout_open_dir);
  FUNC("openConfig", cb_fsl_config_open);
  FUNC("openDb", cb_fsl_db_ctor);
  FUNC("openRepo", cb_fsl_repo_open);
  FUNC("symToRid", cb_fsl_cx_sym2rid);
  FUNC("symToUuid", cb_fsl_cx_sym2uuid);







|







2754
2755
2756
2757
2758
2759
2760
2761
2762
2763
2764
2765
2766
2767
2768
  /* FUNC("closeConfig", cb_fsl_config_close); */
  FUNC("finalize", cb_fsl_cx_finalize);
  FUNC("getUserName", cb_fsl_cx_user_name);
  FUNC("loadBlob", cb_fsl_cx_content_get);
  FUNC("loadManifest",cb_fsl_cx_deck_load);
  FUNC("loginCookieName", cb_fsl_login_cookie_name);
  FUNC("new", cb_fsl_cx_ctor);
  rc = s2_ctor_method_set(se, proto, cwal_value_get_function(v));
  if(rc) goto end;
  FUNC("openCheckout", cb_fsl_checkout_open_dir);
  FUNC("openConfig", cb_fsl_config_open);
  FUNC("openDb", cb_fsl_db_ctor);
  FUNC("openRepo", cb_fsl_repo_open);
  FUNC("symToRid", cb_fsl_cx_sym2rid);
  FUNC("symToUuid", cb_fsl_cx_sym2uuid);
Changes to s2/unit/020-050-buffer.s2.
134
135
136
137
138
139
140










141
142
143
144
145
146
147
    check( "'h''i'", '%1$Q', "h'i" );
    check( 'NULL', '%1$Q', null );

    // check 0-precision characters...
    check( '', '%1$.0c', '*' );
    check( '', '%1$0.0c', '*' );
    check( '  ', '%1$2.0c', '*' ) /* arguable, but currently true */;










}

scope{
    var b = Buffer(100);
    var x = 0, y = 0;
    b << <<<EOF
    for(var i = 0; i < 5; ++i) ++x, --y; 







>
>
>
>
>
>
>
>
>
>







134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
    check( "'h''i'", '%1$Q', "h'i" );
    check( 'NULL', '%1$Q', null );

    // check 0-precision characters...
    check( '', '%1$.0c', '*' );
    check( '', '%1$0.0c', '*' );
    check( '  ', '%1$2.0c', '*' ) /* arguable, but currently true */;

    // check urlencoding/decoding
    check( 'a%20b%26c', '%1$r', 'a b&c' );
    check( 'a b&c', '%1$R', 'a%20b%26c' );
    check( 'a%2zb', '%1$R', 'a%2zb' );

    assert catch {'%1$.1r'.applyFormat()}.message.indexOf('precision')>0;
    assert catch {'%1$1r'.applyFormat()}.message.indexOf('width')>0;
    assert catch {'%1$.1R'.applyFormat()}.message.indexOf('precision')>0;
    assert catch {'%1$1R'.applyFormat()}.message.indexOf('width')>0;
}

scope{
    var b = Buffer(100);
    var x = 0, y = 0;
    b << <<<EOF
    for(var i = 0; i < 5; ++i) ++x, --y; 
Changes to s2/vg.make.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# We keep these in a separate file to avoid a rebuild when changing
# these flags. Damn... some automatic rules foil us here.
########################################################################
# Valgrind sanity checks...
VG := $(call ShakeNMake.CALL.FIND_FILE,valgrind)
ifneq (,$(VG))
VG_REPORT := VG.report.csv
VG_FLAGS ?= --leak-check=full -v --show-reachable=yes --track-origins=yes
#SCRIPT_LIST := $(shell ls -1 test-[0-9]*.s2 | sort)
# Whether or not to collect massif-based stats...
RUN_MASSIF := 1
.PHONY: vg
# Reminder: don't use -A b/c it breaks output buffering tests!
VG_SHELL_FLAGS=--a -v -S
VG_UNIT_RUN_CMD = ./$(f-s2sh.BIN) $(VG_SHELL_FLAGS)
vg: $(f-s2sh.BIN) $(UNIT_GENERATED)
	@echo "Running: $(VG) $(VG_FLAGS) $(VG_UNIT_RUN_CMD) ..."; \
	export LD_LIBRARY_PATH="$(TOP_SRCDIR):$${LD_LIBRARY_PATH}"; \
	massif_tmp=tmp.massif; \
	for i in $(sort $(UNIT_SCRIPT_LIST)) $(UNIT_GENERATED); do \
		vgout=$$i.vg; \













|







1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
# We keep these in a separate file to avoid a rebuild when changing
# these flags. Damn... some automatic rules foil us here.
########################################################################
# Valgrind sanity checks...
VG := $(call ShakeNMake.CALL.FIND_FILE,valgrind)
ifneq (,$(VG))
VG_REPORT := VG.report.csv
VG_FLAGS ?= --leak-check=full -v --show-reachable=yes --track-origins=yes
#SCRIPT_LIST := $(shell ls -1 test-[0-9]*.s2 | sort)
# Whether or not to collect massif-based stats...
RUN_MASSIF := 1
.PHONY: vg
# Reminder: don't use -A b/c it breaks output buffering tests!
VG_SHELL_FLAGS=--a -S
VG_UNIT_RUN_CMD = ./$(f-s2sh.BIN) $(VG_SHELL_FLAGS)
vg: $(f-s2sh.BIN) $(UNIT_GENERATED)
	@echo "Running: $(VG) $(VG_FLAGS) $(VG_UNIT_RUN_CMD) ..."; \
	export LD_LIBRARY_PATH="$(TOP_SRCDIR):$${LD_LIBRARY_PATH}"; \
	massif_tmp=tmp.massif; \
	for i in $(sort $(UNIT_SCRIPT_LIST)) $(UNIT_GENERATED); do \
		vgout=$$i.vg; \
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71



72
73
74
75
76
77
78
79







80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
	$(patsubst %,unit/%,*.vg *._out *.massif *.db *~ *.uncompressed *.z *.zip *.2) \
	$(patsubst %,unit2/%,*.vg *._out *.massif *.db *~ *.uncompressed *.z *.zip *.2) \
	)
# 'vg' proxies which tweak various s2/cwal-level optimizations...
#vgp: VG_SHELL_FLAGS+=--p
#vgp: VG_REPORT:=VG.report-p.csv
#vgp: vg
vgr: VG_SHELL_FLAGS+=--R -S
vgr: VG_REPORT:=VG.report-r.csv
vgr: vg
vgs: VG_SHELL_FLAGS+=--S
vgs: VG_REPORT:=VG.report-s.csv
vgs: vg
#vgt: VG_SHELL_FLAGS+=--t
#vgt: VG_REPORT:=VG.report-t.csv
#vgt: vg
vgrs: VG_SHELL_FLAGS+=--R --S
vgrs: VG_REPORT:=VG.report-rs.csv
vgrs: vg



# vgrst: VG_SHELL_FLAGS+=--r --s --t
# vgrst: VG_REPORT:=VG.report-rst.csv
# vgrst: vg
# vgprst: VG_SHELL_FLAGS+=--r --s --t --p
# vgprst: VG_REPORT:=VG.report-prst.csv
# vgprst: vg
VG_REPORT_MEGA := VG.report-mega.csv
CLEAN_FILES += $(VG_REPORT_MEGA)







vgall:
	@start=$$(date); \
	echo "Start time: $$start"; \
	$(MAKE) vg && $(MAKE) vgr && $(MAKE) vgs && $(MAKE) vgrs || exit; \
	echo "Run time: $$start - $$(date)"; \
	rm -f $(VG_REPORT_MEGA); \
	for i in VG.report*.csv; do \
		test -s $$i || continue; \
		echo $$i | grep t-mega >/dev/null && continue; \
		echo "Report: $$i"; \
		cut -d, -f1,2,4,5 $$i | tr ',' '\t'; \
	done > $(VG_REPORT_MEGA); \
	echo "Consolidated valgrind report is in [$(VG_REPORT_MEGA)]."

endif
#/$(VG)
########################################################################







|


|





|


>
>
>








>
>
>
>
>
>
>



|













53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
	$(patsubst %,unit/%,*.vg *._out *.massif *.db *~ *.uncompressed *.z *.zip *.2) \
	$(patsubst %,unit2/%,*.vg *._out *.massif *.db *~ *.uncompressed *.z *.zip *.2) \
	)
# 'vg' proxies which tweak various s2/cwal-level optimizations...
#vgp: VG_SHELL_FLAGS+=--p
#vgp: VG_REPORT:=VG.report-p.csv
#vgp: vg
vgr: VG_SHELL_FLAGS+=--R -C -S
vgr: VG_REPORT:=VG.report-r.csv
vgr: vg
vgs: VG_SHELL_FLAGS+=--S -R -C
vgs: VG_REPORT:=VG.report-s.csv
vgs: vg
#vgt: VG_SHELL_FLAGS+=--t
#vgt: VG_REPORT:=VG.report-t.csv
#vgt: vg
vgrs: VG_SHELL_FLAGS+=--R -C --S
vgrs: VG_REPORT:=VG.report-rs.csv
vgrs: vg
vgrsc: VG_SHELL_FLAGS+=--R --C --S
vgrsc: VG_REPORT:=VG.report-rsc.csv
vgrsc: vg
# vgrst: VG_SHELL_FLAGS+=--r --s --t
# vgrst: VG_REPORT:=VG.report-rst.csv
# vgrst: vg
# vgprst: VG_SHELL_FLAGS+=--r --s --t --p
# vgprst: VG_REPORT:=VG.report-prst.csv
# vgprst: vg
VG_REPORT_MEGA := VG.report-mega.csv
CLEAN_FILES += $(VG_REPORT_MEGA)
VG_COMMANDS := \
	$(MAKE) vg \
	&& $(MAKE) vgr \
	&& $(MAKE) vgs \
	&& $(MAKE) vgrs \
	&& $(MAKE) vgrsc

vgall:
	@start=$$(date); \
	echo "Start time: $$start"; \
	$(VG_COMMANDS) || exit; \
	echo "Run time: $$start - $$(date)"; \
	rm -f $(VG_REPORT_MEGA); \
	for i in VG.report*.csv; do \
		test -s $$i || continue; \
		echo $$i | grep t-mega >/dev/null && continue; \
		echo "Report: $$i"; \
		cut -d, -f1,2,4,5 $$i | tr ',' '\t'; \
	done > $(VG_REPORT_MEGA); \
	echo "Consolidated valgrind report is in [$(VG_REPORT_MEGA)]."

endif
#/$(VG)
########################################################################