@@ -18695,6 +18695,7 @@ typedef struct JSFunctionDef {
18695
18695
JSAtom func_name; /* JS_ATOM_NULL if no name */
18696
18696
18697
18697
JSVarDef *vars;
18698
+ uint32_t *vars_htab; // indexes into vars[]
18698
18699
int var_size; /* allocated size for vars[] */
18699
18700
int var_count;
18700
18701
JSVarDef *args;
@@ -20517,6 +20518,86 @@ static __exception int emit_push_const(JSParseState *s, JSValue val,
20517
20518
return 0;
20518
20519
}
20519
20520
20521
+ // perl hash; variation of k&r hash with a different magic multiplier
20522
+ // and a final shuffle to improve distribution of the low-order bits
20523
+ static uint32_t hash_bytes(uint32_t h, const void *b, size_t n)
20524
+ {
20525
+ const char *p;
20526
+
20527
+ for (p = b; p < (char *)b + n; p++)
20528
+ h = 33*h + *p;
20529
+ h += h >> 5;
20530
+ return h;
20531
+ }
20532
+
20533
+ static uint32_t hash_atom(JSAtom atom)
20534
+ {
20535
+ return hash_bytes(0, &atom, sizeof(atom));
20536
+ }
20537
+
20538
+ // caveat emptor: the table size must be a power of two in order for
20539
+ // masking to work, and the load factor constant must be an odd number (5)
20540
+ //
20541
+ // f(n)=n+n/t is used to estimate the load factor but changing t to an
20542
+ // even number introduces gaps in the output of f, sometimes "jumping"
20543
+ // over the next power of two; it's at powers of two when the hash table
20544
+ // must be resized
20545
+ static int update_var_htab(JSContext *ctx, JSFunctionDef *fd)
20546
+ {
20547
+ uint32_t i, j, k, m, *p;
20548
+
20549
+ if (fd->var_count < 27) // 27 + 27/5 == 32
20550
+ return 0;
20551
+ k = fd->var_count - 1;
20552
+ m = fd->var_count + fd->var_count/5;
20553
+ if (m & (m - 1)) // unless power of two
20554
+ goto insert;
20555
+ m *= 2;
20556
+ p = js_realloc(ctx, fd->vars_htab, m * sizeof(*fd->vars_htab));
20557
+ if (!p)
20558
+ return -1;
20559
+ for (i = 0; i < m; i++)
20560
+ p[i] = UINT32_MAX;
20561
+ fd->vars_htab = p;
20562
+ k = 0;
20563
+ m--;
20564
+ insert:
20565
+ m = UINT32_MAX >> clz32(m);
20566
+ do {
20567
+ i = hash_atom(fd->vars[k].var_name);
20568
+ j = 1;
20569
+ for (;;) {
20570
+ p = &fd->vars_htab[i & m];
20571
+ if (*p == UINT32_MAX)
20572
+ break;
20573
+ i += j;
20574
+ j += 1; // quadratic probing
20575
+ }
20576
+ *p = k++;
20577
+ } while (k < (uint32_t)fd->var_count);
20578
+ return 0;
20579
+ }
20580
+
20581
+ static int find_var_htab(JSFunctionDef *fd, JSAtom var_name)
20582
+ {
20583
+ uint32_t i, j, m, *p;
20584
+
20585
+ i = hash_atom(var_name);
20586
+ j = 1;
20587
+ m = fd->var_count + fd->var_count/5;
20588
+ m = UINT32_MAX >> clz32(m);
20589
+ for (;;) {
20590
+ p = &fd->vars_htab[i & m];
20591
+ if (*p == UINT32_MAX)
20592
+ return -1;
20593
+ if (fd->vars[*p].var_name == var_name)
20594
+ return *p;
20595
+ i += j;
20596
+ j += 1; // quadratic probing
20597
+ }
20598
+ return -1; // pacify compiler
20599
+ }
20600
+
20520
20601
/* return the variable index or -1 if not found,
20521
20602
add ARGUMENT_VAR_OFFSET for argument variables */
20522
20603
static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
@@ -20531,11 +20612,24 @@ static int find_arg(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
20531
20612
20532
20613
static int find_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
20533
20614
{
20615
+ JSVarDef *vd;
20534
20616
int i;
20535
- for(i = fd->var_count; i-- > 0;) {
20536
- if (fd->vars[i].var_name == name && fd->vars[i].scope_level == 0)
20617
+
20618
+ if (fd->vars_htab) {
20619
+ i = find_var_htab(fd, name);
20620
+ if (i == -1)
20621
+ goto not_found;
20622
+ vd = &fd->vars[i];
20623
+ if (fd->vars[i].scope_level == 0)
20537
20624
return i;
20538
20625
}
20626
+ for(i = fd->var_count; i-- > 0;) {
20627
+ vd = &fd->vars[i];
20628
+ if (vd->var_name == name)
20629
+ if (vd->scope_level == 0)
20630
+ return i;
20631
+ }
20632
+ not_found:
20539
20633
return find_arg(ctx, fd, name);
20540
20634
}
20541
20635
@@ -20710,6 +20804,8 @@ static int add_var(JSContext *ctx, JSFunctionDef *fd, JSAtom name)
20710
20804
memset(vd, 0, sizeof(*vd));
20711
20805
vd->var_name = JS_DupAtom(ctx, name);
20712
20806
vd->func_pool_idx = -1;
20807
+ if (update_var_htab(ctx, fd))
20808
+ return -1;
20713
20809
return fd->var_count - 1;
20714
20810
}
20715
20811
@@ -28410,6 +28506,7 @@ static void js_free_function_def(JSContext *ctx, JSFunctionDef *fd)
28410
28506
JS_FreeAtom(ctx, fd->vars[i].var_name);
28411
28507
}
28412
28508
js_free(ctx, fd->vars);
28509
+ js_free(ctx, fd->vars_htab); // XXX can probably be freed earlier?
28413
28510
for(i = 0; i < fd->arg_count; i++) {
28414
28511
JS_FreeAtom(ctx, fd->args[i].var_name);
28415
28512
}
@@ -32258,6 +32355,7 @@ static JSValue js_create_function(JSContext *ctx, JSFunctionDef *fd)
32258
32355
b->defined_arg_count = fd->defined_arg_count;
32259
32356
js_free(ctx, fd->args);
32260
32357
js_free(ctx, fd->vars);
32358
+ js_free(ctx, fd->vars_htab);
32261
32359
}
32262
32360
b->cpool_count = fd->cpool_count;
32263
32361
if (b->cpool_count) {
0 commit comments