-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathjs_values.c
630 lines (504 loc) · 14.2 KB
/
js_values.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
107
108
109
110
111
112
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
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
#ifdef _WIN32
#include <windows.h>
#include <intrin.h>
#endif
#include <ctype.h>
#include "js.h"
#include "js_db.h"
#include "js_malloc.h"
#include "js_string.h"
value_t js_strtod(uint8_t *buff, int len);
void js_deleteHandle(value_t val);
void deleteDocument(value_t val);
/*
* Expression evaluation produces a value.
* Recursive expression tree evaluation will call 'dispatch' on each node.
* Dispatch calls 'eval_<NodeType>()' which is responsible for returning a value.
* Values from child node dispatches must be abandoned, stored or returned directly by the parent.
* These values may be stored in an lval, (i.e.) an object, array slot, frame slot (symbol).
* All non-atomic values have a refcount which counts the distinct frame slots pointing at the value.
* Stored values have their reference counts incremented.
* Prior (overwritten) values have their reference counts decremented.
* Prior values which reach refcount==0 are deleted.
* All evaluation takes place in the context of some function frame.
* Two special frame slots exist, one for the caller's arg list, and one for 'this'.
*/
bool decrRefCnt (value_t val) {
if (val.refcount)
#ifndef _WIN32
return !__sync_add_and_fetch(val.raw[-1].refCnt, -1);
#else
return !InterlockedDecrement(val.raw[-1].refCnt);
#endif
if (val.weakcount)
#ifndef _WIN32
return !__sync_add_and_fetch(val.raw[-1].weakCnt, -1);
#else
return !InterlockedDecrement(val.raw[-1].weakCnt);
#endif
return false;
if (val.marshaled || val.type == vt_document)
#ifndef _WIN32
return !__sync_add_and_fetch(val.rawDoc->refCnt, -1);
#else
return !InterlockedDecrement(val.rawDoc->refCnt);
#endif
return false;
}
void incrRefCnt (value_t val) {
if (val.refcount) {
#ifndef _WIN32
__sync_fetch_and_add(val.raw[-1].refCnt, 1);
#else
InterlockedIncrement(val.raw[-1].refCnt);
#endif
return;
}
if (val.weakcount) {
#ifndef _WIN32
__sync_fetch_and_add(val.raw[-1].weakCnt, 1);
#else
InterlockedIncrement(val.raw[-1].weakCnt);
#endif
return;
}
if (val.marshaled || val.type == vt_document) {
#ifndef _WIN32
__sync_fetch_and_add(val.rawDoc->refCnt, 1);
#else
InterlockedIncrement(val.rawDoc->refCnt);
#endif
return;
}
}
// assign value
value_t eval_assign(Node *a, environment_t *env)
{
binaryNode *bn = (binaryNode*)a;
bool prev = env->lval;
value_t right, left;
if (evalDebug) printf("node_assign\n");
env->lval = true;
left = dispatch(bn->left, env);
env->lval = prev;
if (left.type != vt_lval) {
fprintf(stderr, "Not lvalue: %s\n", strtype(left));
abandonValue(left);
return makeError(a, env, "not lvalue");
}
right = dispatch(bn->right, env);
return replaceValue(left, right);
}
// delete values
void deleteSlot(value_t *slot) {
deleteValue(*slot);
slot->type = vt_undef;
}
void deleteValue(value_t val) {
uint32_t idx, i;
if (val.type == vt_lval)
val = *val.lval;
if (val.marshaled)
return;
switch (val.type) {
case vt_string: {
if (val.refcount)
js_free(val.raw);
break;
}
case vt_closure: {
for (idx = 1; idx < val.closure->depth; idx++)
abandonScope(val.closure->scope[idx]);
if (decrRefCnt(val.closure->protoObj))
deleteValue(val.closure->protoObj);
if (decrRefCnt(val.closure->obj))
deleteValue(val.closure->obj);
js_free(val.raw);
break;
}
case vt_array: {
for (i=0; i< vec_cnt(val.aval->valuePtr); i++)
if (decrRefCnt(val.aval->valuePtr[i]))
deleteValue(val.aval->valuePtr[i]);
abandonValue(val.aval->obj);
vec_free(val.aval->valuePtr);
js_free(val.raw);
break;
}
case vt_object: {
for (i=0; i< vec_cnt(val.oval->pairsPtr); i++) {
if (decrRefCnt(val.oval->pairsPtr[i].name))
deleteValue(val.oval->pairsPtr[i].name);
if (decrRefCnt(val.oval->pairsPtr[i].value))
deleteValue(val.oval->pairsPtr[i].value);
}
if (decrRefCnt(val.oval->protoChain))
deleteValue(val.oval->protoChain);
deleteValue(*val.oval->baseVal);
vec_free(val.oval->pairsPtr);
js_free(val.raw);
break;
}
case vt_file: {
fclose(val.file);
break;
}
case vt_document: {
deleteDocument(val);
break;
}
default:
break;
}
}
static char vt_control_str[] = "control";
static char vt_endlist_str[] = "endlist";
static char vt_docId_str[] = "docId";
static char vt_txnId_str[] = "txnId";
static char vt_lval_str[] = "lval";
static char vt_centi_str[] = "centi";
static char vt_array_str[] = "array";
static char vt_binary_str[] = "binary";
static char vt_function_str[] = "function";
static char vt_uuid_str[] = "uuid";
static char vt_md5_str[] = "md5";
static char vt_weakref_str[] = "weakReference";
static char vt_string_str[] = "string";
static char vt_int_str[] = "integer";
static char vt_dbl_str[] = "number";
static char vt_nan_str[] = "NaN";
static char vt_inf_str[] = "Infinity";
static char vt_file_str[] = "file";
static char vt_status_str[] = "status";
static char vt_object_str[] = "object";
static char vt_undef_str[] = "undefined";
static char vt_bool_str[] = "boolean";
static char vt_closure_str[] = "function";
static char vt_unknown_str[] = "unknown";
static char vt_date_str[] = "date";
static char vt_null_str[] = "null";
static char vt_propval_str[] = "builtinProp";
static char vt_propfcn_str[] = "builtinFcn";
char *strtype(value_t t) {
switch (t.type) {
case vt_hndl:
switch (t.subType) {
case Hndl_database:
return "hndl:database";
case Hndl_docStore:
return "hndl:docStore";
case Hndl_anyIdx:
return "hndl:index";
case Hndl_artIndex:
return "hndl:artIdx";
case Hndl_btree1Index:
return "hndl:btree1Idx";
case Hndl_btree2Index:
return "hndl:btree2Idx";
case Hndl_colIndex:
return "hndl:colIdx";
case Hndl_cursor:
return "hndl:cursor";
case Hndl_iterator:
return "hndl:iterator";
}
case vt_key: return "key";
case vt_propval: return vt_propval_str;
case vt_propfcn: return vt_propfcn_str;
case vt_docId: return vt_docId_str;
case vt_txnId: return vt_txnId_str;
case vt_string: return vt_string_str;
case vt_int: return vt_int_str;
case vt_bool: return vt_bool_str;
case vt_dbl: return vt_dbl_str;
case vt_file: return vt_file_str;
case vt_status: return vt_status_str;
case vt_undef: return vt_undef_str;
case vt_closure: return vt_closure_str;
case vt_infinite: return vt_inf_str;
case vt_nan: return vt_nan_str;
case vt_object: return vt_object_str;
case vt_date: return vt_date_str;
case vt_null: return vt_null_str;
case vt_control: return vt_control_str;
case vt_endlist: return vt_endlist_str;
case vt_lval: return vt_lval_str;
case vt_centi: return vt_centi_str;
case vt_array: return vt_array_str;
case vt_binary: return vt_binary_str;
case vt_function: return vt_function_str;
case vt_uuid: return vt_uuid_str;
case vt_md5: return vt_md5_str;
case vt_weakref: return vt_weakref_str;
default:;
}
return vt_unknown_str;
}
// clone a marshaled value to an immediate value
value_t cloneValue(value_t val) {
if (val.marshaled)
switch (val.type) {
case vt_string: {
string_t *str = js_dbaddr(val, NULL);
return newString(str->val, str->len);
}
case vt_array:
return cloneArray(val);
case vt_object:
return cloneObject(val);
default:
break;
}
return val;
}
// replace l-value in frame, array, or object
value_t replaceValue(value_t slot, value_t value) {
incrRefCnt(value);
if (slot.type != vt_lval) {
fprintf(stderr, "Not lvalue: %s\n", strtype(slot));
exit(1);
}
if (slot.subType)
return storeArrayValue(slot, value), value;
if (decrRefCnt(*slot.lval))
deleteValue(*slot.lval);
return *slot.lval = value;
}
// replace slot value in frame, array, or object
void replaceSlot(value_t *slot, value_t value) {
incrRefCnt(value);
// if (decrRefCnt(*slot))
// deleteValue(*slot);
*slot = value;
}
// add reference count to frame
void incrScopeCnt(scope_t *scope) {
rawobj_t *raw = (rawobj_t *)scope;
#ifndef _WIN32
__sync_fetch_and_add(raw[-1].refCnt, 1);
#else
InterlockedIncrement(raw[-1].refCnt);
#endif
}
// abandon literals at end of statement
void abandonLiterals(environment_t *env) {
int idx;
while ((idx = vec_cnt(env->literals))) {
if (decrRefCnt(env->literals[idx - 1]))
deleteValue(env->literals[idx - 1]);
vec_size(env->literals)--;
}
}
// abandon scope
void abandonScope(scope_t *scope) {
rawobj_t *raw = (rawobj_t *)scope;
uint32_t idx;
#ifndef _WIN32
if (__sync_add_and_fetch(raw[-1].refCnt, -1))
return;
#else
if (InterlockedDecrement(raw[-1].refCnt))
return;
#endif
// abandon scope values
for (idx = 0; idx < scope->count; idx++)
if (decrRefCnt(scope->values[idx+1]))
deleteValue(scope->values[idx+1]);
// abandon frame values
// skipping first value which was rtnValue
for (idx = 0; idx < scope->frame->count; idx++)
if (decrRefCnt(scope->frame->values[idx+1]))
deleteValue(scope->frame->values[idx+1]);
if (decrRefCnt(scope->frame->thisVal))
deleteValue(scope->frame->thisVal);
if (decrRefCnt(scope->frame->arguments))
deleteValue(scope->frame->arguments);
js_free(scope->frame);
js_free(scope);
}
// compare and abandon value
// return true if they are different objects
bool abandonValueIfDiff(value_t val, value_t test) {
if (!val.refcount)
return true;
if (val.type != test.type || val.raw != test.raw)
if (!*val.raw[-1].refCnt) {
deleteValue(val);
return true;
}
return false;
}
// abandon value
void abandonSlot(value_t *slot) {
abandonValue(*slot);
slot->type = vt_undef;
}
void abandonValue(value_t val) {
bool del = false;
if (val.refcount) {
if (*val.raw[-1].refCnt)
return;
else
del = true;
}
if (val.weakcount) {
if (*val.raw[-1].weakCnt)
return;
else
del = true;
}
if (del)
deleteValue(val);
}
value_t conv2Bool(value_t src, bool abandon) {
value_t result, cond;
result.bits = vt_bool;
result.boolean = false;
if (src.type == vt_object || src.objvalue)
cond = callObjFcn(src, &ValueOfStr, abandon, NULL);
else
cond = src;
switch (cond.type) {
case vt_nan: result.boolean = false; break;
case vt_array: result.boolean = true; break;
case vt_object: result.boolean = true; break;
case vt_infinite: result.boolean = true; break;
case vt_dbl: result.boolean = cond.dbl != 0; break;
case vt_int: result.boolean = cond.nval != 0; break;
case vt_status: result.boolean = cond.status == OK; break;
case vt_file: result.boolean = cond.file != NULL; break;
case vt_closure: result.boolean = cond.closure != NULL; break;
case vt_docId: result.boolean = cond.idBits > 0; break;
case vt_txnId: result.boolean = cond.idBits > 0; break;
case vt_document: result.boolean = true; break;
case vt_undef: result.boolean = false; break;
case vt_null: result.boolean = false; break;
case vt_bool: return cond;
case vt_string: {
string_t *str = js_dbaddr(cond, NULL);
result.boolean = str->len > 0;
break;
}
default: break;
}
return result;
}
value_t conv2Dbl (value_t src, bool abandon) {
value_t result, val;
if (src.type == vt_object || src.objvalue)
val = callObjFcn(src, &ValueOfStr, abandon, NULL);
else
val = src;
result.bits = vt_dbl;
result.dbl = 0;
switch (val.type) {
case vt_undef: result.bits = vt_nan; break;
case vt_dbl: result.dbl = val.dbl; break;
case vt_int: result.dbl = (double)val.nval; break;
case vt_bool: result.dbl = (double)val.boolean; break;
case vt_string: {
string_t *str = js_dbaddr(val, NULL);
result = js_strtod(str->val, str->len);
break;
}
default: break;
}
abandonValueIfDiff(val, src);
if (abandon)
abandonValue(src);
return result;
}
value_t conv2Int (value_t src, bool abandon) {
value_t result, val;
if (src.marshaled)
val = src;
else if (src.type == vt_object || src.objvalue)
val = callObjFcn(src, &ValueOfStr, abandon, NULL);
else
val = src;
result.bits = vt_int;
result.nval = 0;
switch (val.type) {
case vt_undef: result.bits = vt_nan; break;
case vt_int: result.nval = val.nval; break;
case vt_dbl: result.nval = (int64_t)val.dbl; break;
case vt_bool: result.nval = val.boolean; break;
case vt_null: result.nval = 0; break;
case vt_string: {
string_t *str = js_dbaddr(val, NULL);
result = js_strtod(str->val, str->len);
break;
}
case vt_array: {
value_t *values;
uint32_t cnt;
if (val.marshaled) {
dbarray_t *dbaval = js_dbaddr(val, NULL);
values = dbaval->valueArray;
cnt = dbaval->cnt;
} else {
values = val.aval->valuePtr;
cnt = vec_cnt(values);
}
if (cnt>1)
break;
if (!cnt)
return result.nval = 0, result;
return conv2Int(values[0], false);
}
default:
result.bits = vt_nan;
}
abandonValueIfDiff(val, src);
if (abandon)
abandonValue(src);
return result;
}
// convert an arbitrary value to a string value
value_t conv2Str (value_t v, bool abandon, bool quote) {
value_t ans[1], original = v;
if (v.type == vt_document)
v = getDocObject(v);
if (v.type != vt_string)
v = callObjFcn(v, &ToStringStr, abandon, NULL);
if (v.type == vt_undef)
v = newString(strtype(original), -1);
if (quote) {
value_t q;
q.bits = vt_string;
q.addr = &QuoteStr;
*ans = q;
valueCat (ans, v, abandon);
valueCat (ans, q, false);
return *ans;
}
return v;
}
// concatenate string to string value_t
void valueCat (value_t *left, value_t right, bool abandon) {
string_t *rightstr = js_dbaddr(right, NULL);
valueCatStr(left, rightstr->val, rightstr->len);
if (abandon)
abandonValue(right);
}
// concatenate string to string value_t
void valueCatStr (value_t *left, uint8_t *rightval, uint32_t rightlen) {
string_t *leftstr = js_dbaddr(*left, NULL), *valstr;
uint32_t len = rightlen + leftstr->len;
value_t val;
// can we extend existing string value?
if (left->refcount && left->raw[-1].refCnt[0] < 2)
if (js_size(left->raw) - sizeof(string_t) > len) {
memcpy (leftstr->val + leftstr->len, rightval, rightlen);
leftstr->len += rightlen;
leftstr->val[len] = 0;
return;
}
val = newString(NULL, len);
valstr = val.addr;
valstr->val[len] = 0;
memcpy(valstr->val, leftstr->val, leftstr->len);
memcpy(valstr->val + leftstr->len, rightval, rightlen);
valstr->len = len;
replaceSlot(left, val);
}