diff --git a/.gitignore b/.gitignore index 47deb17..4f55b8e 100644 --- a/.gitignore +++ b/.gitignore @@ -12,6 +12,7 @@ /build /traces +/docs /todo/* /src/pkg/.zig-cache diff --git a/examples/demo/c/da.ts b/examples/demo/c/da.ts new file mode 100644 index 0000000..f72babb --- /dev/null +++ b/examples/demo/c/da.ts @@ -0,0 +1,132 @@ +import type { ptr } from './stdlib.h'; + +import { mem, brk } from './memory.h'; +import { $, malloc, free, memcpy, typedef } from './stdlib.h'; + +const da_t = typedef([ + { name: 'items', type: 'ptr' }, + { name: 'count', type: 'int' }, + { name: 'capacity', type: 'int' } +]); + +function da_create(): ptr { + const p = malloc(da_t.size); + mem[p + $(da_t, 'items')] = 0; + mem[p + $(da_t, 'count')] = 0; + mem[p + $(da_t, 'capacity')] = 0; + return p; +} + +function da_append(da: ptr, item: unknown): void { + let count = mem[da + $(da_t, 'count')] as number; + let cap = mem[da + $(da_t, 'capacity')] as number; + let items = mem[da + $(da_t, 'items')] as ptr; + + if (count >= cap) { + const new_cap = cap === 0 ? 256 : cap * 2; + const new_items = malloc(new_cap); + if (items) { + memcpy(new_items, items, count); + free(items, cap); + } + mem[da + $(da_t, 'items')] = new_items; + mem[da + $(da_t, 'capacity')] = new_cap; + items = new_items; + } + + mem[items + count] = item; + mem[da + $(da_t, 'count')] = count + 1; +} + +function da_get(da: ptr, i: number): unknown { + return mem[(mem[da + $(da_t, 'items')] as ptr) + i]; +} + +function da_set(da: ptr, i: number, item: unknown): void { + mem[(mem[da + $(da_t, 'items')] as ptr) + i] = item; +} + +function da_pop(da: ptr): unknown { + const count = (mem[da + $(da_t, 'count')] as number) - 1; + mem[da + $(da_t, 'count')] = count; + const items = mem[da + $(da_t, 'items')] as ptr; + const item = mem[items + count]; + delete mem[items + count]; + return item; +} + +function da_count(da: ptr): number { + return mem[da + $(da_t, 'count')] as number; +} + +function da_capacity(da: ptr): number { + return mem[da + $(da_t, 'capacity')] as number; +} + +function da_free(da: ptr): void { + const items = mem[da + $(da_t, 'items')] as ptr; + const cap = mem[da + $(da_t, 'capacity')] as number; + if (items) free(items, cap); + free(da, da_t.size); +} + +const names = da_create(); + +da_append(names, 'tsoding'); +da_append(names, 'ritchie'); +da_append(names, 'kernighan'); +da_append(names, 'pike'); +da_append(names, 'carmack'); + +console.log(da_get(names, 0)); +console.log(da_get(names, 4)); +console.log(da_count(names)); +console.log(da_capacity(names)); + +da_set(names, 1, 'thompson'); +console.log(da_get(names, 1)); + +console.log(da_pop(names)); +console.log(da_pop(names)); +console.log(da_count(names)); + +da_append(names, 'lol'); +da_append(names, 'handmade'); +console.log(da_count(names)); + +for (let i = 0; i < da_count(names); i++) { + console.log(i, da_get(names, i)); +} + +const nums = da_create(); +for (let i = 0; i < 1000; i++) { + da_append(nums, i * i); +} +console.log(da_count(nums)); +console.log(da_capacity(nums)); +console.log(da_get(nums, 999)); + +da_free(nums); + +const vec3_t = typedef([ + { name: 'x', type: 'float' }, + { name: 'y', type: 'float' }, + { name: 'z', type: 'float' } +]); + +const v = malloc(vec3_t.size); +mem[v + $(vec3_t, 'x')] = 1.0; +mem[v + $(vec3_t, 'y')] = 2.0; +mem[v + $(vec3_t, 'z')] = 3.0; + +console.log(mem[v + $(vec3_t, 'x')]); +console.log(mem[v + $(vec3_t, 'y')]); +console.log(mem[v + $(vec3_t, 'z')]); + +console.log('brk:', brk); +console.log('expecting error:'); + +da_free(names); + +if (Math.random() < 0.5) da_free(names); +else console.log(da_count(names)); diff --git a/examples/demo/c/memory.h.ts b/examples/demo/c/memory.h.ts new file mode 100644 index 0000000..b886a38 --- /dev/null +++ b/examples/demo/c/memory.h.ts @@ -0,0 +1,44 @@ +export const freedSet = new Set(); + +const _mem: Record = {}; + +function fmtPtr(n: number): string { + return '0x' + (0x100000000 + n).toString(16); +} + +export function die(addr: number, msg: string): never { + const bin = process.argv[1]?.split('/').pop() || 'a.out'; + const pid = process.pid; + const ptr = fmtPtr(addr); + + console.error(`${bin}(${pid}): malloc: *** error for object ${ptr}: ${msg}`); + console.error(`${bin}(${pid}): malloc: *** set a breakpoint in malloc_error_break to debug`); + process.exit(134); +} + +export const mem: Record = new Proxy(_mem, { + get(target, prop) { + const key = Number(prop); + if (!isNaN(key) && freedSet.has(key)) { + if (Ant.raw.stack?.includes('_free')) return target[key]; + die(key, 'use of deallocated pointer'); + } + return target[key]; + }, + set(target, prop, value) { + const key = Number(prop); + if (!isNaN(key) && freedSet.has(key)) { + die(key, 'use of deallocated pointer'); + } + target[key] = value; + return true; + }, + deleteProperty(target, prop) { + const key = Number(prop); + if (!isNaN(key)) delete target[key]; + return true; + } +}); + +export let brk: number = 0; +export const arena = { mem: _mem, brk, freedSet }; diff --git a/examples/demo/c/stdlib.h.ts b/examples/demo/c/stdlib.h.ts new file mode 100644 index 0000000..47ef76b --- /dev/null +++ b/examples/demo/c/stdlib.h.ts @@ -0,0 +1,42 @@ +import { arena, freedSet, die } from './memory.h'; + +export type ptr = number; +export type union = 'ptr' | 'int' | 'float' | 'char*' | 'unknown'; + +export function malloc(size: number): ptr { + const p = arena.brk; + arena.brk += size; + for (let i = 0; i < size; i++) freedSet.delete(p + i); + return p; +} + +export function dealloc(p: ptr, i: ptr) { + delete arena.mem[p + i]; + freedSet.add(p + i); +} + +export function free(p: ptr, size: number): void { + if (freedSet.has(p)) die(p, 'pointer being freed was not allocated'); + for (let i = 0; i < size; i++) dealloc(p, i); +} + +export function memcpy(dst: ptr, src: ptr, n: number): void { + for (let i = 0; i < n; i++) arena.mem[dst + i] = arena.mem[src + i]; +} + +export interface struct_def { + fields: Record; + size: number; +} + +export function typedef(fields: { name: T; type: union }[]): struct_def { + const result = {} as Record; + fields.forEach((f, i) => { + result[f.name] = { offset: i, type: f.type }; + }); + return { fields: result, size: fields.length }; +} + +export function $(def: struct_def, field: T): number { + return def.fields[field].offset; +} diff --git a/examples/demo/fibonacci_memo.js b/examples/demo/fibonacci_memo.js index 8e8df54..9462d25 100644 --- a/examples/demo/fibonacci_memo.js +++ b/examples/demo/fibonacci_memo.js @@ -6,8 +6,8 @@ function fibonacci(n, memo = {}) { } const start = performance.now(); -const result = fibonacci(30); +const result = fibonacci(35); const end = performance.now(); -console.log(`fibonacci(30) = ${result}`); +console.log(`fibonacci(35) = ${result}`); console.log(`Time: ${(end - start).toFixed(4)} ms (${((end - start) * 1000).toFixed(2)} µs)`); diff --git a/examples/demo/fibonacci_recursive.js b/examples/demo/fibonacci_recursive.js index 1b92f6a..9a4d01e 100644 --- a/examples/demo/fibonacci_recursive.js +++ b/examples/demo/fibonacci_recursive.js @@ -4,8 +4,8 @@ function fib(n) { } const start = performance.now(); -const result = fib(30); +const result = fib(35); const end = performance.now(); -console.log(`fibonacci(30) = ${result}`); +console.log(`fibonacci(35) = ${result}`); console.log(`Time: ${(end - start).toFixed(2)} ms`); diff --git a/examples/demo/fibonacci_tco.js b/examples/demo/fibonacci_tco.js index d99c523..8623f1a 100644 --- a/examples/demo/fibonacci_tco.js +++ b/examples/demo/fibonacci_tco.js @@ -4,8 +4,8 @@ function fib(n, a = 0, b = 1) { } const start = performance.now(); -const result = fib(30); +const result = fib(35); const end = performance.now(); -console.log(`fibonacci(30) = ${result}`); +console.log(`fibonacci(35) = ${result}`); console.log(`Time: ${(end - start).toFixed(4)} ms (${((end - start) * 1000).toFixed(2)} µs)`); diff --git a/examples/npm/pidtree/ant.lockb b/examples/npm/pidtree/ant.lockb new file mode 100644 index 0000000000000000000000000000000000000000..6a852304dae0c540c1d1b78f86f7288233651bc3 GIT binary patch literal 272 zcmeZq_YPoWU|;}Y1_lNO2L=WPTLuP(1_lNO4n_!u$${k2aY1HENl|KQMoCFQv6a4l zQEGZ-aY<37US2_NR setTimeout(r, 100)); + + const childTree = await pidtree(self, { root: true }); + if (!childTree.includes(childPid)) { + console.log('FAIL — child pid %d not found in tree: %o', childPid, childTree); + child.kill(); + process.exit(1); + } + console.log(' OK — child pid %d found in tree', childPid); + + console.log('3. Testing { root: false } excludes root...'); + const noRoot = await pidtree(self); + if (noRoot.includes(self)) { + console.log('FAIL — root pid should be excluded'); + child.kill(); + process.exit(1); + } + if (!noRoot.includes(childPid)) { + console.log('FAIL — child pid %d not found without root: %o', childPid, noRoot); + child.kill(); + process.exit(1); + } + console.log(' OK — root excluded, child present'); + + console.log('4. Testing { advanced: true } format...'); + const advanced = await pidtree(self, { root: true, advanced: true }); + const selfEntry = advanced.find(e => e.pid === self); + const childEntry = advanced.find(e => e.pid === childPid); + if (!selfEntry || !childEntry) { + console.log('FAIL — missing entries in advanced output: %o', advanced); + child.kill(); + process.exit(1); + } + if (childEntry.ppid !== self) { + console.log('FAIL — child ppid should be %d, got %d', self, childEntry.ppid); + child.kill(); + process.exit(1); + } + console.log(' OK — advanced format correct (child ppid=%d)', childEntry.ppid); + + console.log('5. Testing pid -1 lists all pids...'); + const all = await pidtree(-1); + if (!Array.isArray(all) || all.length < 2) { + console.log('FAIL — expected many pids, got %d', all ? all.length : 0); + child.kill(); + process.exit(1); + } + console.log(' OK — got %d pids from system', all.length); + + console.log('6. Testing stdio: "inherit" on inner spawn...'); + const inheritChild = spawn('echo', ['hello'], { stdio: 'inherit' }); + if (inheritChild.stdin !== null || inheritChild.stdout !== null || inheritChild.stderr !== null) { + console.log('FAIL — stdio streams should be null with inherit'); + child.kill(); + process.exit(1); + } + await new Promise(r => inheritChild.on('close', r)); + console.log(' OK — inherited stdio, streams are null'); + + child.kill(); + await new Promise(r => child.on('close', r)); + + console.log('\nAll good.'); +} + +run().catch(err => { + console.error('FAIL —', err.message); + process.exit(1); +}); diff --git a/examples/npm/pidtree/package.json b/examples/npm/pidtree/package.json new file mode 100644 index 0000000..959ce99 --- /dev/null +++ b/examples/npm/pidtree/package.json @@ -0,0 +1,12 @@ +{ + "name": "pidtree", + "type": "module", + "main": "index.js", + "scripts": { + "start": "ant index.js" + }, + "dependencies": { + "pidtree": "^0.6.0" + }, + "devDependencies": {} +} \ No newline at end of file diff --git a/examples/results.txt b/examples/results.txt index c24c044..9ddbb67 100644 --- a/examples/results.txt +++ b/examples/results.txt @@ -557,7 +557,7 @@ compat-table/es6/Reflect.construct.RegExp-subclassing.js: OK compat-table/es6/Reflect.construct.js: OK compat-table/es6/Reflect.construct.new-target.js: OK compat-table/es6/Reflect.construct.third-argument.js: OK -compat-table/es6/Reflect.defineProperty.js: failed +compat-table/es6/Reflect.defineProperty.js: OK compat-table/es6/Reflect.deleteProperty.js: OK compat-table/es6/Reflect.getOwnPropertyDescriptor.js: OK compat-table/es6/Reflect.getPrototypeOf.js: OK @@ -615,7 +615,7 @@ compat-table/es6/Symbol.global-registry.js: OK compat-table/es6/Symbol.hidden-keys.js: OK compat-table/es6/Symbol.js: OK compat-table/es6/Symbol.no-coerce.js: failed -compat-table/es6/Symbol.no-new.js: failed +compat-table/es6/Symbol.no-new.js: OK compat-table/es6/Symbol.prototype.js: OK compat-table/es6/Symbol.typeof.js: OK compat-table/es6/WeakMap.accept-primitives.js: OK @@ -1251,7 +1251,7 @@ compat-table/es2020/DataView.prototype.getBigUint64.js: TypeError: undefined is compat-table/es2020/Promise.allSettled.js: TypeError: undefined is not a function compat-table/es2020/String.prototype.matchAll.js: TypeError: undefined is not a function compat-table/es2020/String.prototype.matchAll.throws-non-global.js: failed -compat-table/es2020/globalThis.descriptor.js: failed +compat-table/es2020/globalThis.descriptor.js: OK compat-table/es2020/globalThis.js: OK compat-table/es2020/nullish-coalescing.js: OK compat-table/es2020/optional-chaining.bracket.js: OK diff --git a/examples/test262/index.js b/examples/test262/index.js index 4e7c06a..1cfd98f 100755 --- a/examples/test262/index.js +++ b/examples/test262/index.js @@ -189,24 +189,50 @@ function buildScreen() { } else if (state.mode === 'memory') { const mem = Ant.stats(); - lines.push(`${c.cyan}Arena${c.reset}`); - lines.push(` Used: ${c.bold}${fmt(mem.arenaUsed)}${c.reset}`); - lines.push(` Size: ${c.bold}${fmt(mem.arenaSize)}${c.reset}`); + lines.push(`${c.cyan}Pools${c.reset}`); + lines.push(` Rope: ${c.bold}${fmt(mem.pools.rope.used)}${c.reset} / ${fmt(mem.pools.rope.capacity)} (${mem.pools.rope.blocks} blocks)`); + lines.push( + ` String: ${c.bold}${fmt(mem.pools.string.used)}${c.reset} / ${fmt(mem.pools.string.capacity)} (${mem.pools.string.blocks} blocks)` + ); + lines.push( + ` Symbol: ${c.bold}${fmt(mem.pools.symbol.used)}${c.reset} / ${fmt(mem.pools.symbol.capacity)} (${mem.pools.symbol.blocks} blocks)` + ); + lines.push( + ` Bigint: ${c.bold}${fmt(mem.pools.bigint.used)}${c.reset} / ${fmt(mem.pools.bigint.capacity)} (${mem.pools.bigint.blocks} blocks)` + ); + lines.push(` Total: ${c.bold}${fmt(mem.pools.totalUsed)} / ${fmt(mem.pools.totalCapacity)}${c.reset}`); lines.push(''); - if (mem.external) { - lines.push(`${c.cyan}External${c.reset}`); - lines.push(` Buffers: ${c.bold}${fmt(mem.external.buffers)}${c.reset}`); - lines.push(` Code: ${c.bold}${fmt(mem.external.code)}${c.reset}`); - lines.push(` Collections: ${c.bold}${fmt(mem.external.collections)}${c.reset}`); - lines.push(` Total: ${c.bold}${fmt(mem.external.total)}${c.reset}`); - lines.push(''); - } + lines.push(`${c.cyan}Alloc${c.reset}`); + lines.push(` Objects: ${c.bold}${fmt(mem.alloc.objects)}${c.reset} (${mem.alloc.objectCount} objects)`); + lines.push(` Overflow: ${c.bold}${fmt(mem.alloc.overflow)}${c.reset}`); + lines.push(` ExtraSlots: ${c.bold}${fmt(mem.alloc.extraSlots)}${c.reset}`); + lines.push(` Promises: ${c.bold}${fmt(mem.alloc.promises)}${c.reset}`); + lines.push(` Proxies: ${c.bold}${fmt(mem.alloc.proxies)}${c.reset}`); + lines.push(` Exotic: ${c.bold}${fmt(mem.alloc.exotic)}${c.reset}`); + lines.push(` Arrays: ${c.bold}${fmt(mem.alloc.arrays)}${c.reset}`); + lines.push(` Shapes: ${c.bold}${fmt(mem.alloc.shapes)}${c.reset}`); + lines.push(` Closures: ${c.bold}${fmt(mem.alloc.closures)}${c.reset}`); + lines.push(` Upvalues: ${c.bold}${fmt(mem.alloc.upvalues)}${c.reset}`); + lines.push(` PropRefs: ${c.bold}${fmt(mem.alloc.propRefs)}${c.reset}`); + lines.push(` Total: ${c.bold}${fmt(mem.alloc.total)}${c.reset}`); + lines.push(''); + + lines.push(`${c.cyan}External${c.reset}`); + lines.push(` Buffers: ${c.bold}${fmt(mem.external.buffers)}${c.reset}`); + lines.push(` Code: ${c.bold}${fmt(mem.external.code)}${c.reset}`); + lines.push(` Total: ${c.bold}${fmt(mem.external.total)}${c.reset}`); + lines.push(''); - if (mem.intern) { - lines.push(`${c.cyan}Intern Table${c.reset}`); - lines.push(` Strings: ${c.bold}${mem.intern.count}${c.reset}`); - lines.push(` Bytes: ${c.bold}${fmt(mem.intern.bytes)}${c.reset}`); + lines.push(`${c.cyan}Intern Table${c.reset}`); + lines.push(` Strings: ${c.bold}${mem.intern.count}${c.reset}`); + lines.push(` Bytes: ${c.bold}${fmt(mem.intern.bytes)}${c.reset}`); + lines.push(''); + + if (mem.vm) { + lines.push(`${c.cyan}VM${c.reset}`); + lines.push(` Stack: ${c.bold}${mem.vm.stackUsed} / ${mem.vm.stackSize}${c.reset}`); + lines.push(` Frames: ${c.bold}${mem.vm.framesUsed} / ${mem.vm.maxFrames}${c.reset}`); lines.push(''); } @@ -215,10 +241,7 @@ function buildScreen() { if (mem.virtualSize) { lines.push(` Virtual: ${c.bold}${fmt(mem.virtualSize)}${c.reset}`); } - lines.push(''); - - lines.push(`${c.cyan}C Stack${c.reset}`); - lines.push(` Max: ${c.bold}${fmt(mem.cstack)}${c.reset}`); + lines.push(` C Stack: ${c.bold}${fmt(mem.cstack)}${c.reset}`); lines.push(''); while (lines.length < rows - 2) { diff --git a/examples/tui/index.js b/examples/tui/index.js index 0796dad..cb7f509 100644 --- a/examples/tui/index.js +++ b/examples/tui/index.js @@ -390,7 +390,7 @@ function showMemoryModal() { modal(screen, { id: 'memory', width: 40, - height: 24, + height: 10, title: 'Memory Usage', titleStyle: colors.bold + colors.yellow, borderStyle: box.rounded, @@ -405,36 +405,10 @@ function showMemoryModal() { const mem = Ant.stats(); let y = 1; - buf.writeStyled(ox, oy + y++, `${colors.cyan}Arena${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Used: ${colors.bold}${fmt(mem.arenaUsed)}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Size: ${colors.bold}${fmt(mem.arenaSize)}${codes.reset}`); - y++; - - if (mem.external) { - buf.writeStyled(ox, oy + y++, `${colors.cyan}External${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Buffers: ${colors.bold}${fmt(mem.external.buffers)}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Code: ${colors.bold}${fmt(mem.external.code)}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Collections: ${colors.bold}${fmt(mem.external.collections)}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Total: ${colors.bold}${fmt(mem.external.total)}${codes.reset}`); - y++; - } - - if (mem.intern) { - buf.writeStyled(ox, oy + y++, `${colors.cyan}Intern Table${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Strings: ${colors.bold}${mem.intern.count}${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Bytes: ${colors.bold}${fmt(mem.intern.bytes)}${codes.reset}`); - y++; - } - - buf.writeStyled(ox, oy + y++, `${colors.cyan}Process${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` RSS: ${colors.bold}${fmt(mem.rss)}${codes.reset}`); - if (mem.virtualSize) { - buf.writeStyled(ox, oy + y++, ` Virtual: ${colors.bold}${fmt(mem.virtualSize)}${codes.reset}`); - } - y++; - - buf.writeStyled(ox, oy + y++, `${colors.cyan}C Stack${codes.reset}`); - buf.writeStyled(ox, oy + y++, ` Max: ${colors.bold}${fmt(mem.cstack)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` Pools: ${colors.bold}${fmt(mem.pools.totalUsed)} / ${fmt(mem.pools.totalCapacity)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` Alloc: ${colors.bold}${fmt(mem.alloc.total)}${codes.reset} (${mem.alloc.objectCount} objects)`); + buf.writeStyled(ox, oy + y++, ` External: ${colors.bold}${fmt(mem.external.total)}${codes.reset}`); + buf.writeStyled(ox, oy + y++, ` RSS: ${colors.bold}${fmt(mem.rss)}${codes.reset}`); y++; buf.writeStyled(ox, oy + y, colors.dim + ' m/Esc: Close' + codes.reset); diff --git a/include/ant.h b/include/ant.h index 80831df..f4e5417 100644 --- a/include/ant.h +++ b/include/ant.h @@ -17,8 +17,6 @@ #define STR_PROTO "__proto__" #define STR_PROTO_LEN 9 -#define ANT_LIMIT_SIZE_CACHE 16384 - #define ANT_STRING(s) js_mkstr(js, s, sizeof(s) - 1) #define ANT_PTR(ptr) js_mknum((double)(uintptr_t)(ptr)) #define ANT_COPY(buf, len, s) cpy(buf, len, s, sizeof(s) - 1) @@ -37,6 +35,7 @@ ant_t *js_create(void *buf, size_t len); ant_t *js_create_dynamic(); ant_value_t js_glob(ant_t *); +void js_mark_constructor(ant_value_t value, bool is_constructor); // TODO: improve naming ant_value_t js_eval_bytecode(ant_t *, const char *, size_t); @@ -56,13 +55,6 @@ int32_t js_to_int32(double d); bool js_chkargs(ant_value_t *, int, const char *); void js_set_filename(ant_t *, const char *); void js_stats(ant_t *, size_t *total, size_t *min, size_t *cstacksize); -size_t js_getbrk(ant_t *); - -ant_handle_t js_root(ant_t *, ant_value_t); -ant_value_t js_deref(ant_t *, ant_handle_t); - -void js_unroot(ant_t *, ant_handle_t); -void js_root_update(ant_t *, ant_handle_t, ant_value_t); ant_value_t js_mkundef(void); ant_value_t js_mknull(void); @@ -92,7 +84,6 @@ bool js_iter( void *udata ); -uint64_t js_sym_id(ant_value_t sym); const char *js_sym_desc(ant_t *js, ant_value_t sym); const char *js_sym_key(ant_value_t sym); @@ -101,9 +92,11 @@ ant_value_t js_symbol_to_string(ant_t *js, ant_value_t sym); ant_value_t js_get_sym(ant_t *, ant_value_t obj, ant_value_t sym); ant_value_t js_mkobj(ant_t *); +ant_value_t js_mkobj_with_inobj_limit(ant_t *, uint8_t inobj_limit); ant_value_t js_newobj(ant_t *); ant_value_t js_mkarr(ant_t *); ant_value_t js_mkstr(ant_t *, const void *, size_t); +ant_value_t js_mkstr_permanent(ant_t *, const void *, size_t); ant_value_t js_mkbigint(ant_t *, const char *digits, size_t len, bool negative); ant_value_t js_mksym(ant_t *, const char *desc); ant_value_t js_mkfun(ant_value_t (*fn)(ant_t *, ant_value_t *, int)); @@ -115,11 +108,13 @@ ant_offset_t js_mkprop_fast_off(ant_t *js, ant_value_t obj, const char *key, siz void js_set(ant_t *, ant_value_t, const char *, ant_value_t); void js_set_sym(ant_t *, ant_value_t obj, ant_value_t sym, ant_value_t val); void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v); -bool js_del(ant_t *, ant_value_t obj, const char *key); void js_merge_obj(ant_t *, ant_value_t dst, ant_value_t src); void js_arr_push(ant_t *, ant_value_t arr, ant_value_t val); -void js_set_proto(ant_t *, ant_value_t obj, ant_value_t proto); +void js_set_proto(ant_value_t obj, ant_value_t proto); +void js_set_proto_wb(ant_t *js, ant_value_t obj, ant_value_t proto); +void js_set_proto_init(ant_value_t obj, ant_value_t proto); +ant_value_t js_propref_load(ant_t *js, ant_offset_t handle); ant_value_t js_setprop(ant_t *, ant_value_t obj, ant_value_t key, ant_value_t val); ant_value_t js_setprop_nonconfigurable(ant_t *, ant_value_t obj, const char *key, size_t keylen, ant_value_t val); @@ -129,6 +124,8 @@ ant_value_t js_tostring_val(ant_t *js, ant_value_t value); uint8_t vtype(ant_value_t val); size_t vdata(ant_value_t val); +ant_object_t *js_obj_ptr(ant_value_t val); +bool js_is_constructor(ant_t *js, ant_value_t value); double js_getnum(ant_value_t val); char *js_getstr(ant_t *js, ant_value_t val, size_t *len); @@ -158,7 +155,7 @@ int js_getffi(ant_value_t val); void js_resolve_promise(ant_t *js, ant_value_t promise, ant_value_t value); void js_reject_promise(ant_t *js, ant_value_t promise, ant_value_t value); void js_check_unhandled_rejections(ant_t *js); -void js_process_promise_handlers(ant_t *js, uint32_t promise_id); +void js_process_promise_handlers(ant_t *js, ant_value_t promise); void js_setup_import_meta(ant_t *js, const char *filename); typedef ant_value_t (*js_getter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len); @@ -166,16 +163,13 @@ typedef bool (*js_setter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t typedef bool (*js_deleter_fn)(ant_t *js, ant_value_t obj, const char *key, size_t key_len); typedef ant_value_t (*js_keys_fn)(ant_t *js, ant_value_t obj); -void js_set_getter(ant_t *js, ant_value_t obj, js_getter_fn getter); -void js_set_setter(ant_t *js, ant_value_t obj, js_setter_fn setter); -void js_set_deleter(ant_t *js, ant_value_t obj, js_deleter_fn deleter); -void js_set_keys(ant_t *js, ant_value_t obj, js_keys_fn keys); - -ant_value_t js_get_slot(ant_t *js, ant_value_t obj, internal_slot_t slot); -void js_set_slot(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t value); +void js_set_getter(ant_value_t obj, js_getter_fn getter); +void js_set_setter(ant_value_t obj, js_setter_fn setter); +void js_set_deleter(ant_value_t obj, js_deleter_fn deleter); +void js_set_keys(ant_value_t obj, js_keys_fn keys); -bool js_is_slot_prop(ant_offset_t header); -ant_offset_t js_next_prop(ant_offset_t header); -ant_offset_t js_loadoff(ant_t *js, ant_offset_t off); +ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot); +void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value); +void js_set_slot_wb(ant_t *, ant_value_t obj, internal_slot_t slot, ant_value_t value); #endif diff --git a/include/arena.h b/include/arena.h index 9424c39..bbd6ad0 100644 --- a/include/arena.h +++ b/include/arena.h @@ -1,6 +1,7 @@ #ifndef ARENA_H #define ARENA_H +#include "common.h" #include #include #include @@ -16,26 +17,22 @@ #endif #define ANT_ARENA_MIN (32 * 1024) -#define ANT_ARENA_MAX (256ULL * 1024 * 1024 * 1024) - -#define ANT_ARENA_THRESHOLD (256ULL * 1024 * 1024) -#define ARENA_GROW_INCREMENT (8ULL * 1024 * 1024) - -static inline void *mantissa_chk(void *p, const char *func) { - if (p && ((uintptr_t)p >> 47) != 0) { - fprintf( - stderr, - "FATAL: %s returned pointer %p outside 47-bit NaN-boxing range\n" - "Please report this issue with your OS/architecture details.\n", func, p - ); - abort(); - } - - return p; -} - -#define ant_calloc(size) mantissa_chk(calloc(1, size), "calloc") -#define ant_realloc(ptr, size) mantissa_chk(realloc(ptr, size), "realloc") +#define ANT_ARENA_MAX (64ULL * 1024 * 1024 * 1024) + +#define ANT_ARENA_THRESHOLD (256ULL * 1024 * 1024) +#define ARENA_GROW_INCREMENT (8ULL * 1024 * 1024) +#define ANT_CLOSURE_ARENA_MAX (2ULL * 1024 * 1024 * 1024) + +typedef struct { + uint8_t *base; + size_t committed; + size_t reserved; + size_t watermark; + size_t live_count; + size_t elem_size; + size_t epoch_offset; + void *free_list; +} ant_fixed_arena_t; #ifdef _WIN32 @@ -51,10 +48,13 @@ static inline int ant_arena_commit(void *base, size_t old_size, size_t new_size) } static inline void ant_arena_free(void *base, size_t reserved_size) { - (void)reserved_size; if (base) VirtualFree(base, 0, MEM_RELEASE); } +static inline void *ant_os_alloc(size_t size) { + return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); +} + static inline int ant_arena_decommit(void *base, size_t old_size, size_t new_size) { if (new_size >= old_size) return 0; void *decommit_start = (char *)base + new_size; @@ -72,18 +72,21 @@ static inline void *ant_arena_reserve(size_t max_size) { static inline int ant_arena_commit(void *base, size_t old_size, size_t new_size) { if (new_size <= old_size) return 0; - + long page_size_long = sysconf(_SC_PAGESIZE); if (page_size_long <= 0) { errno = (page_size_long == -1 && errno == 0) ? EINVAL : errno; return -1; } - + size_t page_size = (size_t)page_size_long; size_t old_pages = ((old_size + page_size - 1) / page_size) * page_size; size_t new_pages = ((new_size + page_size - 1) / page_size) * page_size; - + if (new_pages <= old_pages) return 0; +#ifdef __APPLE__ + madvise((char *)base + old_pages, new_pages - old_pages, MADV_FREE_REUSE); +#endif return mprotect((char *)base + old_pages, new_pages - old_pages, PROT_READ | PROT_WRITE); } @@ -91,29 +94,110 @@ static inline void ant_arena_free(void *base, size_t reserved_size) { if (base) munmap(base, reserved_size); } +static inline void *ant_os_alloc(size_t size) { + void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); + return (p == MAP_FAILED) ? NULL : p; +} + static inline int ant_arena_decommit(void *base, size_t old_size, size_t new_size) { if (new_size >= old_size) return 0; - + long page_size_long = sysconf(_SC_PAGESIZE); if (page_size_long <= 0) return -1; - + size_t page_size = (size_t)page_size_long; size_t new_pages = ((new_size + page_size - 1) / page_size) * page_size; size_t old_pages = ((old_size + page_size - 1) / page_size) * page_size; - + if (new_pages >= old_pages) return 0; - + void *decommit_start = (char *)base + new_pages; size_t decommit_size = old_pages - new_pages; - - if (mprotect(decommit_start, decommit_size, PROT_NONE) != 0) return -1; + #ifdef __APPLE__ - if (madvise(decommit_start, decommit_size, MADV_FREE) != 0) return -1; + madvise(decommit_start, decommit_size, MADV_FREE_REUSABLE); + if (mprotect(decommit_start, decommit_size, PROT_NONE) != 0) return -1; #else + if (mprotect(decommit_start, decommit_size, PROT_NONE) != 0) return -1; if (madvise(decommit_start, decommit_size, MADV_DONTNEED) != 0) return -1; #endif return 0; } #endif -#endif \ No newline at end of file + +static inline bool fixed_arena_init(ant_fixed_arena_t *a, size_t elem_size, size_t epoch_offset, size_t max_size) { + a->base = (uint8_t *)ant_arena_reserve(max_size); + if (!a->base) return false; + + a->reserved = max_size; + a->watermark = 0; + a->live_count = 0; + a->elem_size = elem_size; + a->epoch_offset = epoch_offset; + a->free_list = NULL; + + size_t initial = 2ULL * 1024 * 1024; + if (initial > max_size) initial = max_size; + if (ant_arena_commit(a->base, 0, initial) != 0) { + ant_arena_free(a->base, max_size); + a->base = NULL; + return false; + } + + a->committed = initial; + return true; +} + +static inline void fixed_arena_destroy(ant_fixed_arena_t *a) { + if (a->base) ant_arena_free(a->base, a->reserved); + a->base = NULL; + a->committed = 0; + a->reserved = 0; + a->watermark = 0; + a->live_count = 0; + a->free_list = NULL; +} + +static inline void *fixed_arena_alloc(ant_fixed_arena_t *a) { + if (a->free_list) { + void *p = a->free_list; + a->free_list = *(void **)p; + memset(p, 0, a->elem_size); + a->live_count++; + return p; + } + + size_t needed = a->watermark + a->elem_size; + if (needed > a->committed) { + size_t grow = a->committed / 4; + if (grow < (64ULL * 1024)) grow = 64ULL * 1024; + if (grow > ARENA_GROW_INCREMENT) grow = ARENA_GROW_INCREMENT; + size_t new_committed = a->committed + grow; + if (new_committed > a->reserved) return NULL; + if (ant_arena_commit(a->base, a->committed, new_committed) != 0) return NULL; + a->committed = new_committed; + } + + void *p = a->base + a->watermark; + a->watermark = needed; + memset(p, 0, a->elem_size); + a->live_count++; + return p; +} + +static inline void fixed_arena_free_elem(ant_fixed_arena_t *a, void *p) { + if (!p) return; + *(void **)p = a->free_list; + a->free_list = p; + if (a->live_count > 0) a->live_count--; +} + +static inline bool fixed_arena_contains(const ant_fixed_arena_t *a, const void *ptr) { + uintptr_t p = (uintptr_t)ptr; + uintptr_t lo = (uintptr_t)a->base; + if (p < lo || p >= lo + a->watermark) return false; + return ((p - lo) % a->elem_size) == 0; +} + +#endif diff --git a/include/common.h b/include/common.h index a029122..76e7d9e 100644 --- a/include/common.h +++ b/include/common.h @@ -1,11 +1,9 @@ #ifndef COMMON_H #define COMMON_H -#define SLOTMASK ~(((ant_offset_t) ~0) >> 1) -#define CONSTMASK (~(((ant_offset_t) ~0) >> 1) >> 1) -#define ARRMASK (~(((ant_offset_t) ~0) >> 1) >> 2) -#define NONCONFIGMASK (~(((ant_offset_t) ~0) >> 1) >> 3) -#define FLAGMASK (SLOTMASK | CONSTMASK | ARRMASK | NONCONFIGMASK) +#include +#include +#include typedef enum { SLOT_NONE = 0, @@ -24,9 +22,6 @@ typedef enum { SLOT_PROTO, SLOT_FUNC_PROTO, SLOT_ASYNC_PROTO, - SLOT_FROZEN, - SLOT_SEALED, - SLOT_EXTENSIBLE, SLOT_BUFFER, SLOT_TARGET_FUNC, SLOT_NAME, @@ -50,7 +45,6 @@ typedef enum { SLOT_ITER_STATE, SLOT_BYTECODE, SLOT_ENTRIES, - SLOT_DENSE_BUF, SLOT_SETTLED, SLOT_WT_ON_MESSAGE, SLOT_WT_ONCE_MESSAGE, @@ -76,4 +70,20 @@ typedef enum { BUILTIN_OBJECT = 1 } builtin_fn_id_t; +static inline void *mantissa_chk(void *p, const char *func) { + if (!p || ((uintptr_t)p >> 47) == 0) goto ok; + + fprintf( + stderr, + "FATAL: %s returned pointer %p outside 47-bit NaN-boxing range\n" + "Please report this issue with your OS/architecture details.\n", func, p + ); + + abort(); + ok: return p; +} + +#define ant_calloc(size) mantissa_chk(calloc(1, size), "calloc") +#define ant_realloc(ptr, size) mantissa_chk(realloc(ptr, size), "realloc") + #endif diff --git a/include/descriptors.h b/include/descriptors.h index 7d2780d..328919d 100644 --- a/include/descriptors.h +++ b/include/descriptors.h @@ -10,7 +10,7 @@ typedef struct descriptor_entry { uint64_t key; - ant_offset_t obj_off; + uintptr_t obj_off; ant_offset_t sym_off; char *prop_name; size_t prop_len; @@ -26,11 +26,11 @@ typedef struct descriptor_entry { extern descriptor_entry_t *desc_registry; -descriptor_entry_t *lookup_descriptor(ant_t *js, ant_offset_t obj_off, const char *key, size_t klen); -descriptor_entry_t *lookup_sym_descriptor(ant_offset_t obj_off, ant_offset_t sym_off); +descriptor_entry_t *lookup_descriptor(ant_value_t obj, const char *key, size_t klen); +descriptor_entry_t *lookup_sym_descriptor(ant_value_t obj, ant_offset_t sym_off); -uint64_t make_desc_key(ant_offset_t obj_off, const char *key, size_t klen); -uint64_t make_sym_desc_key(ant_offset_t obj_off, ant_offset_t sym_off); +uint64_t make_desc_key(ant_value_t obj, const char *key, size_t klen); +uint64_t make_sym_desc_key(ant_value_t obj, ant_offset_t sym_off); void js_set_descriptor(ant_t *js, ant_value_t obj, const char *key, size_t klen, int flags); void js_set_getter_desc(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t getter, int flags); diff --git a/include/errors.h b/include/errors.h index 1a55dc2..b3ad0d9 100644 --- a/include/errors.h +++ b/include/errors.h @@ -30,6 +30,7 @@ bool print_unhandled_promise_rejection(ant_t *js, ant_value_t value); void js_clear_error_site(ant_t *js); void js_print_stack_trace_vm(ant_t *js, FILE *stream); void js_set_error_site_from_vm_top(ant_t *js); +void js_capture_stack(ant_t *js, ant_value_t err_obj); void js_set_error_site_from_bc( ant_t *js, sv_func_t *func, @@ -44,6 +45,7 @@ void js_set_error_site( __attribute__((format(printf, 4, 5))) ant_value_t js_create_error(ant_t *js, js_err_type_t err_type, ant_value_t props, const char *fmt, ...); +ant_value_t js_capture_raw_stack(ant_t *js); ant_value_t js_throw(ant_t *js, ant_value_t value); #define js_mkerr(js, ...) js_create_error(js, JS_ERR_TYPE, js_mkundef(), __VA_ARGS__) diff --git a/include/esm/loader.h b/include/esm/loader.h index 3b8d344..d31337a 100644 --- a/include/esm/loader.h +++ b/include/esm/loader.h @@ -23,7 +23,6 @@ typedef struct ant_module_t { } ant_module_t; void js_esm_cleanup_module_cache(void); -void js_esm_gc_roots(void (*visit)(void *ctx, ant_value_t *val), void *ctx); ant_value_t js_esm_import_sync(ant_t *js, ant_value_t specifier); ant_value_t js_esm_make_file_url(ant_t *js, const char *path); diff --git a/include/gc.h b/include/gc.h index 66bcc49..9559b39 100644 --- a/include/gc.h +++ b/include/gc.h @@ -1,63 +1,50 @@ -#ifndef GC_H -#define GC_H +#ifndef ANT_GC_H +#define ANT_GC_H -#include +#include "internal.h" #include +#include + +#define GC_MIN_TICK 1024 +#define GC_FORCE_INTERVAL_MS 50 +#define GC_NURSERY_THRESHOLD 4096 +#define GC_MAJOR_EVERY_N_MINOR 8 + +#define GC_HEAP_GROWTH(n) ((n) + (n) / 2) + +#define GC_OBJ_TYPE_MASK (JS_TYPE_FLAG(T_OBJ) \ + | JS_TYPE_FLAG(T_ARR) \ + | JS_TYPE_FLAG(T_PROMISE) \ + | JS_TYPE_FLAG(T_GENERATOR)) + +typedef struct gc_func_mark_profile { + bool enabled; + uint64_t collections; + uint64_t func_visits; + uint64_t child_edges; + uint64_t const_slots; + uint64_t time_ns; +} gc_func_mark_profile_t; + +void gc_run(ant_t *js); +void gc_run_minor(ant_t *js); +void gc_maybe(ant_t *js); +void gc_remember_add(ant_t *js, ant_object_t *obj); + +void gc_func_mark_profile_enable(bool enabled); +void gc_func_mark_profile_reset(void); + +extern bool gc_disabled; +gc_func_mark_profile_t gc_func_mark_profile_get(void); + +static inline void gc_write_barrier(ant_t *js, ant_object_t *writer_obj, ant_value_t new_val) { + if (writer_obj->generation != 1 || new_val <= NANBOX_PREFIX) return; + uint8_t type = (new_val >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; + if (type == T_FUNC) gc_remember_add(js, writer_obj); + else if ((1u << type) & GC_OBJ_TYPE_MASK) { + ant_object_t *ref = (ant_object_t *)(uintptr_t)(new_val & NANBOX_DATA_MASK); + if (ref && ref->generation == 0) gc_remember_add(js, writer_obj); + } +} -#define ROPE_FLAG (1ULL << 63) -#define ROPE_DEPTH_SHIFT 56 -#define ROPE_DEPTH_MASK 0x7FULL -#define ROPE_MAX_DEPTH 64 -#define ROPE_FLATTEN_THRESHOLD (32 * 1024) - -typedef struct { - ant_offset_t header; - ant_value_t left; - ant_value_t right; - ant_value_t cached; -} rope_node_t; - -#define GC_FWD_LOAD_FACTOR 70 -#define GC_ROOTS_INITIAL_CAP 32 -#define GC_CB(ret, name, arg) ret (*name)(void *ctx, arg old) - -#define GC_CTX void *ctx -#define GC_FWD_OFF GC_CB(ant_offset_t, fwd_off, ant_offset_t) -#define GC_FWD_VAL GC_CB(ant_value_t, fwd_val, ant_value_t) -#define GC_WEAK_OFF GC_CB(ant_offset_t, weak_off, ant_offset_t) -#define GC_OP_VAL void (*op_val)(void *ctx, ant_value_t *val) - -#define GC_FWD_ARGS GC_FWD_VAL, GC_CTX -#define GC_RESERVE_ARGS ant_t *js, GC_FWD_OFF, GC_FWD_ARGS -#define GC_UPDATE_ARGS ant_t *js, GC_FWD_OFF, GC_WEAK_OFF, GC_FWD_ARGS -#define GC_OP_VAL_ARGS GC_OP_VAL, GC_CTX - -#define FWD_EMPTY ((ant_offset_t)~0) -#define FWD_TOMBSTONE ((ant_offset_t)~1) - -#define GC_BIGINT_HEADER_SHIFT 4 -#define GC_SYM_HEADER_SHIFT 4 - -#define GC_BIGINT_HEADER_LOW_MASK ((ant_offset_t)((1u << GC_BIGINT_HEADER_SHIFT) - 1u)) -#define GC_SYM_HEADER_LOW_MASK ((ant_offset_t)((1u << GC_SYM_HEADER_SHIFT) - 1u)) -#define GC_SYM_HEAP_FIXED (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uintptr_t)) - -#ifdef _WIN32 -#define RELEASE_PAGES(p, sz) VirtualAlloc(p, sz, MEM_RESET, PAGE_READWRITE) -#elif defined(__APPLE__) -#define RELEASE_PAGES(p, sz) madvise(p, sz, MADV_FREE) -#else -#define RELEASE_PAGES(p, sz) madvise(p, sz, MADV_DONTNEED) #endif - -#define GC_HEAP_TYPE_MASK ( \ - (1u << T_OBJ) | (1u << T_PROP) | (1u << T_STR) | \ - (1u << T_ARR) | (1u << T_PROMISE) | (1u << T_BIGINT) | (1u << T_GENERATOR) | \ - (1u << T_SYMBOL)) - -extern uint32_t gc_epoch_counter; - -void js_gc_maybe(ant_t *js); -void js_gc_throttle(bool enabled); - -#endif \ No newline at end of file diff --git a/include/gc/modules.h b/include/gc/modules.h new file mode 100644 index 0000000..6ed9820 --- /dev/null +++ b/include/gc/modules.h @@ -0,0 +1,23 @@ +#ifndef ANT_GC_MODULES_H +#define ANT_GC_MODULES_H + +#include "types.h" + +typedef void (*gc_mark_fn)(ant_t *js, ant_value_t v); + +void gc_mark_timers(ant_t *js, gc_mark_fn mark); +void gc_mark_ffi(ant_t *js, gc_mark_fn mark); +void gc_mark_fetch(ant_t *js, gc_mark_fn mark); +void gc_mark_fs(ant_t *js, gc_mark_fn mark); +void gc_mark_child_process(ant_t *js, gc_mark_fn mark); +void gc_mark_readline(ant_t *js, gc_mark_fn mark); +void gc_mark_process(ant_t *js, gc_mark_fn mark); +void gc_mark_navigator(ant_t *js, gc_mark_fn mark); +void gc_mark_server(ant_t *js, gc_mark_fn mark); +void gc_mark_events(ant_t *js, gc_mark_fn mark); +void gc_mark_lmdb(ant_t *js, gc_mark_fn mark); +void gc_mark_symbols(ant_t *js, gc_mark_fn mark); +void gc_mark_esm(ant_t *js, gc_mark_fn mark); +void gc_mark_worker_threads(ant_t *js, gc_mark_fn mark); + +#endif diff --git a/include/gc/objects.h b/include/gc/objects.h new file mode 100644 index 0000000..7901e03 --- /dev/null +++ b/include/gc/objects.h @@ -0,0 +1,21 @@ +#ifndef ANT_GC_OBJECTS_H +#define ANT_GC_OBJECTS_H + +#include "types.h" +#include "object.h" + +#define ANT_GC_DEAD 0xFF + +typedef void (*gc_str_mark_fn)(ant_t *js, ant_value_t v); +bool gc_obj_is_marked(const ant_object_t *obj); + +void gc_mark_value(ant_t *js, ant_value_t v); +void gc_objects_run(ant_t *js, gc_str_mark_fn str_mark); +void gc_objects_run_minor(ant_t *js, gc_str_mark_fn str_mark); +void gc_object_free(ant_t *js, ant_object_t *obj); +void gc_pin_existing_objects(ant_t *js); + +void gc_root_pending_promise(ant_object_t *obj); +void gc_unroot_pending_promise(ant_object_t *obj); + +#endif diff --git a/include/gc/refs.h b/include/gc/refs.h new file mode 100644 index 0000000..b21d3da --- /dev/null +++ b/include/gc/refs.h @@ -0,0 +1,8 @@ +#ifndef ANT_GC_REFS_H +#define ANT_GC_REFS_H + +#include "types.h" + +void gc_refs_shrink(ant_t *js); + +#endif diff --git a/include/gc/roots.h b/include/gc/roots.h new file mode 100644 index 0000000..6e8ec76 --- /dev/null +++ b/include/gc/roots.h @@ -0,0 +1,28 @@ +#ifndef ANT_GC_ROOTS_H +#define ANT_GC_ROOTS_H + +#include "types.h" +#include +#include + +#define GC_MAX_STATIC_ROOTS 64 + +typedef void (*gc_root_visitor_t)(ant_t *js, ant_value_t v); + +size_t gc_root_scope(ant_t *js); +bool gc_push_root(ant_t *js, ant_value_t *slot); + +void gc_register_root(ant_value_t *slot); +void gc_pop_roots(ant_t *js, size_t mark); +void gc_visit_roots(ant_t *js, gc_root_visitor_t visitor); + +#define GC_ROOT_PIN(js, slot) do { \ + bool _gc_root_ok = gc_push_root((js), &(slot)); \ + assert(_gc_root_ok && "gc_push_root failed"); \ + (void)_gc_root_ok; \ +} while (0) + +#define GC_ROOT_SAVE(name, js) size_t name = gc_root_scope((js)) +#define GC_ROOT_RESTORE(js, mark) gc_pop_roots((js), (mark)) + +#endif diff --git a/include/gc/ropes.h b/include/gc/ropes.h new file mode 100644 index 0000000..854d209 --- /dev/null +++ b/include/gc/ropes.h @@ -0,0 +1,11 @@ +#ifndef ANT_GC_ROPES_H +#define ANT_GC_ROPES_H + +#include "types.h" +#include + +void gc_ropes_begin(ant_t *js); +void gc_ropes_sweep(ant_t *js); +bool gc_ropes_mark(const void *ptr); + +#endif diff --git a/include/gc/strings.h b/include/gc/strings.h new file mode 100644 index 0000000..4b9d2e6 --- /dev/null +++ b/include/gc/strings.h @@ -0,0 +1,11 @@ +#ifndef ANT_GC_STRINGS_H +#define ANT_GC_STRINGS_H + +#include "types.h" +#include + +void gc_strings_begin(ant_t *js); +void gc_strings_mark(ant_t *js, const void *ptr); +void gc_strings_sweep(ant_t *js); + +#endif diff --git a/include/internal.h b/include/internal.h index dca3b1c..2ecf986 100644 --- a/include/internal.h +++ b/include/internal.h @@ -2,15 +2,14 @@ #define ANT_INTERNAL_H #include "ant.h" -#include "gc.h" +#include "object.h" +#include "pool.h" +#include "arena.h" #include "esm/loader.h" #include #include -typedef struct sv_vm sv_vm_t; -typedef struct sv_func sv_func_t; - // An IEEE 754 double-precision float is a 64-bit value with bits laid out like: // // 1 Sign bit @@ -68,6 +67,9 @@ typedef struct sv_func sv_func_t; #define PROTO_WALK_F_OBJECT_ONLY (1u << 0) #define PROTO_WALK_F_LOOKUP (1u << 1) +#define ROPE_MAX_DEPTH 64 +#define ROPE_FLATTEN_THRESHOLD (32 * 1024) + #define T_EMPTY (NANBOX_PREFIX | ((ant_value_t)T_SENTINEL << NANBOX_TYPE_SHIFT) | 0xDEADULL) #define T_SPECIAL_OBJECT_MASK (JS_TYPE_FLAG(T_OBJ) | JS_TYPE_FLAG(T_ARR)) #define T_NEEDS_PROTO_FALLBACK (JS_TYPE_FLAG(T_FUNC) | JS_TYPE_FLAG(T_ARR) | JS_TYPE_FLAG(T_PROMISE)) @@ -80,12 +82,11 @@ typedef struct sv_func sv_func_t; enum { // heap-resident - T_OBJ = 0, - T_PROP = 1, - T_STR = 2, + T_OBJ = 0, + T_STR = 1, + T_ARR = 2, // objects - T_ARR = 3, T_FUNC, T_CFUNC, T_CLOSURE, @@ -106,7 +107,13 @@ enum { T_FFI, T_NTARG, - T_SENTINEL = 31 + // collections + T_MAP, + T_SET, + T_WEAKMAP, + T_WEAKSET, + + T_SENTINEL = NANBOX_TYPE_MASK }; typedef struct { @@ -118,20 +125,29 @@ typedef struct { bool valid; } js_error_site_t; -struct ant { +struct ant_isolate_t { sv_vm_t *vm; + ant_module_t *module; + ant_object_t *objects; + ant_object_t *permanent_objects; + + ant_fixed_arena_t obj_arena; + ant_prop_ref_t *prop_refs; - #ifdef ANT_JIT - void *jit_ctx; - uint32_t jit_active_depth; - #endif + ant_fixed_arena_t closure_arena; + ant_fixed_arena_t upvalue_arena; + + ant_value_t **c_roots; + size_t c_root_count; + size_t c_root_cap; const char *code; const char *filename; - - uint64_t sym_counter; - js_error_site_t errsite; + + #ifdef ANT_JIT + void *jit_ctx; + #endif ant_value_t global; ant_value_t object; @@ -139,11 +155,9 @@ struct ant { ant_value_t new_target; ant_value_t current_func; ant_value_t length_str; + ant_value_t thrown_value; + ant_value_t thrown_stack; - uint8_t *mem; - ant_offset_t size; - ant_offset_t brk; - struct { void *base; void *main_base; @@ -151,18 +165,50 @@ struct ant { size_t limit; } cstk; + struct { + uint64_t counter; + struct sym_registry_entry *registry; + + ant_value_t iterator_proto; + ant_value_t array_iterator_proto; + ant_value_t string_iterator_proto; + } sym; + ant_offset_t max_size; - ant_value_t thrown_value; - ant_value_t thrown_stack; - ant_offset_t gc_alloc_since; + ant_offset_t prop_refs_len; + ant_offset_t prop_refs_cap; + js_error_site_t errsite; - ant_value_t *gc_roots; - ant_handle_t gc_roots_len; - ant_handle_t gc_roots_cap; + struct { + ant_pool_t rope; + ant_pool_t symbol; + ant_pool_t permanent; + ant_class_pool_t bigint; + ant_class_pool_t string; + } pool; - bool owns_mem; - bool needs_gc; + struct { + size_t closures; + size_t upvalues; + } alloc_bytes; + size_t gc_last_live; + size_t gc_pool_alloc; + size_t gc_pool_last_live; + + ant_object_t *objects_old; + size_t old_live_count; + size_t minor_gc_count; + + ant_object_t **remember_set; + size_t remember_set_len; + size_t remember_set_cap; + + #ifdef ANT_JIT + uint32_t jit_active_depth; + #endif + + bool owns_mem; bool fatal_error; bool thrown_exists; }; @@ -201,36 +247,22 @@ static inline bool is_empty_slot(ant_value_t v) { return v == T_EMPTY; } -static inline ant_offset_t loadoff(ant_t *js, ant_offset_t off) { - assert(off + sizeof(ant_offset_t) <= js->brk); ant_offset_t val; - memcpy(&val, &js->mem[off], sizeof(val)); return val; -} - -static inline ant_value_t loadval(ant_t *js, ant_offset_t off) { - return *(ant_value_t *)(&js->mem[off]); -} - -static inline bool is_arr_off(ant_t *js, ant_offset_t off) { - return (loadoff(js, off) & ARRMASK) != 0; -} - bool is_internal_prop(const char *key, ant_offset_t klen); size_t uint_to_str(char *buf, size_t bufsize, uint64_t val); -void js_gc_reserve_roots(GC_RESERVE_ARGS); -void js_gc_update_roots(GC_UPDATE_ARGS); -void js_gc_visit_frame_funcs(ant_t *js, void (*visitor)(void *, sv_func_t *), void *ctx); - -ant_offset_t esize(ant_offset_t w); ant_value_t tov(double d); - double tod(ant_value_t v); double js_to_number(ant_t *js, ant_value_t arg); -ant_value_t resolveprop(ant_t *js, ant_value_t v); -ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t flags); +bool js_obj_ensure_prop_capacity(ant_object_t *obj, uint32_t needed); +bool js_obj_ensure_unique_shape(ant_object_t *obj); + +ant_value_t js_propref_load(ant_t *js, ant_offset_t handle); +ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs); +ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs); ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v); +ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode); ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v); ant_value_t js_create_import_meta(ant_t *js, const char *filename, bool is_main); @@ -240,32 +272,37 @@ ant_value_t coerce_to_str(ant_t *js, ant_value_t v); ant_value_t coerce_to_str_concat(ant_t *js, ant_value_t v); ant_value_t get_ctor_species_value(ant_t *js, ant_value_t ctor); -bool is_rope(ant_t *js, ant_value_t value); bool proto_chain_contains(ant_t *js, ant_value_t obj, ant_value_t proto_target); bool same_ctor_identity(ant_t *js, ant_value_t a, ant_value_t b); js_intern_stats_t js_intern_stats(void); +const char *intern_string(const char *str, size_t len); js_cstr_t js_to_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack_size); +ant_value_t lkp_interned_val(ant_t *js, ant_value_t obj, const char *search_intern); +ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len); + ant_offset_t lkp(ant_t *js, ant_value_t obj, const char *buf, size_t len); ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *buf, size_t len); + ant_offset_t lkp_sym(ant_t *js, ant_value_t obj, ant_offset_t sym_off); ant_offset_t lkp_sym_proto(ant_t *js, ant_value_t obj, ant_offset_t sym_off); + ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len); ant_offset_t vstrlen(ant_t *js, ant_value_t value); ant_offset_t str_len_fast(ant_t *js, ant_value_t str); -ant_offset_t js_alloc(ant_t *js, size_t size); ant_value_t mkarr(ant_t *js); ant_value_t mkval(uint8_t type, uint64_t data); ant_value_t mkobj(ant_t *js, ant_offset_t parent); +ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit); ant_value_t rope_flatten(ant_t *js, ant_value_t rope); ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj); ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t len); ant_value_t js_delete_sym_prop(ant_t *js, ant_value_t obj, ant_value_t sym); -bool is_proxy(ant_t *js, ant_value_t obj); +bool is_proxy(ant_value_t obj); bool strict_eq_values(ant_t *js, ant_value_t l, ant_value_t r); ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, ant_value_t *args, int argc); @@ -280,6 +317,9 @@ ant_value_t js_to_primitive(ant_t *js, ant_value_t value, int hint); ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r); ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r); +bool js_is_prototype_of(ant_t *js, ant_value_t proto_obj, ant_value_t obj); +ant_value_t builtin_object_isPrototypeOf(ant_t *js, ant_value_t *args, int nargs); + void js_module_eval_ctx_push(ant_t *js, ant_module_t *ctx); void js_module_eval_ctx_pop(ant_t *js, ant_module_t *ctx); @@ -308,4 +348,25 @@ static inline ant_module_format_t js_module_eval_active_format(ant_t *js) { return ctx ? ctx->format : MODULE_EVAL_FORMAT_UNKNOWN; } +static inline bool is_length_key(const char *key, size_t len) { + return len == 6 && !memcmp(key, "length", 6); +} + +static inline bool str_is_heap_rope(ant_value_t value) { + return vtype(value) == T_STR && ((vdata(value) & 1ULL) != 0); +} + +typedef struct { + ant_offset_t len; + char bytes[]; +} ant_flat_string_t; + +typedef struct { + ant_offset_t len; + uint8_t depth; + ant_value_t left; + ant_value_t right; + ant_value_t cached; +} ant_rope_heap_t; + #endif diff --git a/include/modules/child_process.h b/include/modules/child_process.h index 4375a96..e95fc62 100644 --- a/include/modules/child_process.h +++ b/include/modules/child_process.h @@ -1,12 +1,9 @@ #ifndef ANT_CHILD_PROCESS_MODULE_H #define ANT_CHILD_PROCESS_MODULE_H -#include "gc.h" #include "types.h" ant_value_t child_process_library(ant_t *js); - int has_pending_child_processes(void); -void child_process_gc_update_roots(GC_OP_VAL_ARGS); #endif diff --git a/include/modules/collections.h b/include/modules/collections.h index ca9f6f8..76d296f 100644 --- a/include/modules/collections.h +++ b/include/modules/collections.h @@ -3,7 +3,6 @@ #include #include "types.h" -#include "gc.h" typedef struct map_entry { char *key; @@ -48,22 +47,7 @@ typedef struct set_iterator_state { iter_type_t type; } set_iterator_state_t; -typedef struct map_registry_entry { - map_entry_t **head; - ant_offset_t obj_offset; -} map_registry_entry_t; - -typedef struct set_registry_entry { - set_entry_t **head; - ant_offset_t obj_offset; -} set_registry_entry_t; - void init_collections_module(void); -void cleanup_collections_module(void); -size_t collections_get_external_memory(void); - -void collections_gc_reserve_roots(GC_OP_VAL_ARGS); -void collections_gc_update_roots(ant_offset_t (*weak_off)(void *ctx, ant_offset_t old), GC_OP_VAL_ARGS); map_entry_t **get_map_from_obj(ant_t *js, ant_value_t obj); set_entry_t **get_set_from_obj(ant_t *js, ant_value_t obj); diff --git a/include/modules/events.h b/include/modules/events.h index 57df90d..9b9fdc8 100644 --- a/include/modules/events.h +++ b/include/modules/events.h @@ -4,8 +4,6 @@ #include "types.h" void init_events_module(void); -void events_gc_update_roots(void (*op_val)(void *, ant_value_t *), void *ctx); - ant_value_t events_library(ant_t *js); #endif \ No newline at end of file diff --git a/include/modules/fetch.h b/include/modules/fetch.h index a4e581e..3217d89 100644 --- a/include/modules/fetch.h +++ b/include/modules/fetch.h @@ -1,11 +1,7 @@ #ifndef FETCH_H #define FETCH_H -#include "gc.h" - void init_fetch_module(void); -void fetch_gc_update_roots(GC_OP_VAL_ARGS); - int has_pending_fetches(void); #endif diff --git a/include/modules/ffi.h b/include/modules/ffi.h index 566ed7c..038912a 100644 --- a/include/modules/ffi.h +++ b/include/modules/ffi.h @@ -1,12 +1,9 @@ #ifndef ANT_FFI_H #define ANT_FFI_H -#include "gc.h" #include "types.h" ant_value_t ffi_library(ant_t *js); ant_value_t ffi_call_by_index(ant_t *js, unsigned int func_index, ant_value_t *args, int nargs); -void ffi_gc_update_roots(GC_OP_VAL_ARGS); - #endif \ No newline at end of file diff --git a/include/modules/fs.h b/include/modules/fs.h index f23a3b1..b8f4a8f 100644 --- a/include/modules/fs.h +++ b/include/modules/fs.h @@ -1,15 +1,12 @@ #ifndef ANT_FS_MODULE_H #define ANT_FS_MODULE_H -#include "gc.h" #include "types.h" ant_value_t fs_library(ant_t *js); ant_value_t fs_promises_library(ant_t *js); int has_pending_fs_ops(void); - void init_fs_module(void); -void fs_gc_update_roots(GC_OP_VAL_ARGS); #endif diff --git a/include/modules/io.h b/include/modules/io.h index cc9721c..d03a595 100644 --- a/include/modules/io.h +++ b/include/modules/io.h @@ -8,7 +8,7 @@ extern bool io_no_color; typedef struct { - ant_offset_t *visited; + uintptr_t *visited; int count; int capacity; } inspect_visited_t; diff --git a/include/modules/lmdb.h b/include/modules/lmdb.h index 594743c..a1ccbbf 100644 --- a/include/modules/lmdb.h +++ b/include/modules/lmdb.h @@ -1,7 +1,6 @@ #ifndef ANT_LMDB_MODULE_H #define ANT_LMDB_MODULE_H -#include "gc.h" #include "types.h" typedef struct lmdb_env_handle lmdb_env_handle_t; @@ -23,7 +22,6 @@ typedef struct { } lmdb_js_types_t; ant_value_t lmdb_library(ant_t *js); -void lmdb_gc_update_roots(GC_OP_VAL_ARGS); void cleanup_lmdb_module(void); #endif diff --git a/include/modules/navigator.h b/include/modules/navigator.h index d48f767..18a26b0 100644 --- a/include/modules/navigator.h +++ b/include/modules/navigator.h @@ -1,9 +1,6 @@ #ifndef NAVIGATOR_H #define NAVIGATOR_H -#include "gc.h" - void init_navigator_module(void); -void navigator_gc_update_roots(GC_OP_VAL_ARGS); #endif diff --git a/include/modules/process.h b/include/modules/process.h index 8ff6a66..df57f4c 100644 --- a/include/modules/process.h +++ b/include/modules/process.h @@ -1,9 +1,8 @@ #ifndef PROCESS_H #define PROCESS_H -#include "gc.h" +#include "types.h" -void process_gc_update_roots(GC_OP_VAL_ARGS); void process_enable_keypress_events(void); void emit_process_event(const char *event_type, ant_value_t *args, int nargs); diff --git a/include/modules/readline.h b/include/modules/readline.h index 5abfdfb..1d76af5 100644 --- a/include/modules/readline.h +++ b/include/modules/readline.h @@ -1,7 +1,6 @@ #ifndef READLINE_H #define READLINE_H -#include "gc.h" #include "types.h" #include @@ -9,6 +8,5 @@ ant_value_t readline_library(ant_t *js); ant_value_t readline_promises_library(ant_t *js); bool has_active_readline_interfaces(void); -void readline_gc_update_roots(GC_OP_VAL_ARGS); #endif diff --git a/include/modules/regex.h b/include/modules/regex.h index 8fe9022..9a4075c 100644 --- a/include/modules/regex.h +++ b/include/modules/regex.h @@ -2,12 +2,11 @@ #define REGEX_H #include "types.h" -#include "gc.h" void init_regex_module(void); void cleanup_regex_module(void); +void gc_sweep_regex_cache(void); -void regex_gc_update_roots(ant_offset_t (*weak_off)(void *ctx, ant_offset_t old), GC_OP_VAL_ARGS); size_t js_to_pcre2_pattern(const char *src, size_t src_len, char *dst, size_t dst_size); ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs); diff --git a/include/modules/server.h b/include/modules/server.h index 9b1d081..7d3e1df 100644 --- a/include/modules/server.h +++ b/include/modules/server.h @@ -1,9 +1,6 @@ #ifndef SERVER_H #define SERVER_H -#include "gc.h" - void init_server_module(void); -void server_gc_update_roots(GC_OP_VAL_ARGS); #endif diff --git a/include/modules/symbol.h b/include/modules/symbol.h index e84825f..5baa37b 100644 --- a/include/modules/symbol.h +++ b/include/modules/symbol.h @@ -6,10 +6,6 @@ void init_symbol_module(void); void js_define_species_getter(ant_t *js, ant_value_t ctor); -void symbol_gc_update_roots(GC_OP_VAL_ARGS); - -ant_value_t sym_lookup_by_id(uint32_t id); -void sym_gc_update_all(void (*op_val)(void *, ant_value_t *), void *ctx); ant_value_t maybe_call_symbol_method( ant_t *js, ant_value_t target, ant_value_t sym, diff --git a/include/modules/timer.h b/include/modules/timer.h index 5324855..8ba641e 100644 --- a/include/modules/timer.h +++ b/include/modules/timer.h @@ -1,15 +1,13 @@ #ifndef TIMER_H #define TIMER_H -#include "gc.h" #include "types.h" void init_timer_module(void); void process_microtasks(ant_t *js); void process_immediates(ant_t *js); void queue_microtask(ant_t *js, ant_value_t callback); -void queue_promise_trigger(uint32_t promise_id); -void timer_gc_update_roots(GC_OP_VAL_ARGS); +void queue_promise_trigger(ant_value_t promise); int has_pending_timers(void); int has_pending_microtasks(void); diff --git a/include/object.h b/include/object.h new file mode 100644 index 0000000..5b6e59e --- /dev/null +++ b/include/object.h @@ -0,0 +1,110 @@ +#ifndef ANT_OBJECT_H +#define ANT_OBJECT_H + +#include "types.h" +#include "shapes.h" + +#include +#include +#include +#include + +typedef struct { + ant_value_t (*getter)(ant_t *, ant_value_t, const char *, size_t); + bool (*setter)(ant_t *, ant_value_t, const char *, size_t, ant_value_t); + bool (*deleter)(ant_t *, ant_value_t, const char *, size_t); +} ant_exotic_ops_t; + +typedef struct { + ant_value_t value; + ant_value_t trigger_parent; + UT_array *handlers; + uint32_t promise_id; + uint8_t state; + bool has_rejection_handler; + bool processing; + bool unhandled_reported; +} ant_promise_state_t; + +typedef struct { + ant_value_t target; + ant_value_t handler; + bool revoked; +} ant_proxy_state_t; + +typedef struct ant_object { + struct ant_object *next; + + ant_value_t proto; + ant_shape_t *shape; + ant_value_t *overflow_prop; + + const ant_exotic_ops_t *exotic_ops; + ant_value_t (*exotic_keys)(ant_t *, ant_value_t); + + ant_promise_state_t *promise_state; + ant_proxy_state_t *proxy_state; + ant_value_t *extra_slots; + + struct ant_object *gc_pending_next; + ant_value_t inobj[ANT_INOBJ_MAX_SLOTS]; + + union { + struct { ant_value_t *data; uint32_t len; uint32_t cap; } array; + struct { sv_closure_t *closure; } func; + struct { ant_value_t value; } data; + } u; + + uint32_t prop_count; + uint32_t propref_count; + + uint8_t mark_epoch; + uint8_t type_tag; + uint8_t inobj_limit; + uint8_t extra_count; + + uint8_t extensible: 1; + uint8_t frozen: 1; + uint8_t sealed: 1; + uint8_t is_exotic: 1; + uint8_t is_constructor: 1; + uint8_t fast_array: 1; + uint8_t gc_permanent: 1; + uint8_t generation: 1; + uint8_t in_remember_set: 1; + + bool gc_pending_rooted; + uint8_t overflow_cap; +} ant_object_t; + +typedef struct { + uint8_t slot; + ant_value_t value; +} ant_extra_slot_t; + +typedef struct ant_prop_ref { + ant_object_t *obj; + uint32_t slot; + bool valid; +} ant_prop_ref_t; + +static inline uint32_t ant_object_inobj_limit(const ant_object_t *obj) { + if (!obj) return ANT_INOBJ_MAX_SLOTS; + uint32_t limit = obj->inobj_limit; + return (limit > ANT_INOBJ_MAX_SLOTS) ? ANT_INOBJ_MAX_SLOTS : limit; +} + +static inline ant_value_t ant_object_prop_get_unchecked(const ant_object_t *obj, uint32_t slot) { + uint32_t inobj_limit = ant_object_inobj_limit(obj); + return (slot < inobj_limit) + ? obj->inobj[slot] + : obj->overflow_prop[slot - inobj_limit]; +} + +static inline void ant_object_prop_set_unchecked(ant_object_t *obj, uint32_t slot, ant_value_t value) { + uint32_t inobj_limit = ant_object_inobj_limit(obj); + if (slot < inobj_limit) obj->inobj[slot] = value; + else obj->overflow_prop[slot - inobj_limit] = value; +} + +#endif diff --git a/include/pool.h b/include/pool.h new file mode 100644 index 0000000..1200dbb --- /dev/null +++ b/include/pool.h @@ -0,0 +1,118 @@ +#ifndef ANT_POOL_H +#define ANT_POOL_H + +#include "arena.h" +#include "types.h" + +#include +#include +#include + +struct ant_pool_block { + struct ant_pool_block *next; + struct ant_pool_block *prev; + size_t used; + size_t cap; + uint8_t data[]; +}; + +#define ANT_POOL_SIZE_CLASS_COUNT 32 +#define ANT_POOL_ROPE_BLOCK_SIZE (64u * 1024u) +#define ANT_POOL_SYMBOL_BLOCK_SIZE (32u * 1024u) +#define ANT_POOL_BIGINT_BLOCK_SIZE (64u * 1024u) +#define ANT_POOL_STRING_BLOCK_SIZE (128u * 1024u) + +typedef struct { + ant_pool_block_t *head; + ant_pool_block_t *free_head; + size_t block_size; +} ant_pool_t; + +typedef struct { + ant_pool_block_t *head; + ant_pool_block_t *current; + ant_pool_block_t *free_head; + void *slot_free; + size_t block_size; + uint8_t *cursor; + uint8_t *end; + size_t slot_stride; +} ant_pool_bucket_t; + +typedef struct { + ant_pool_t base; + ant_pool_bucket_t classes[ANT_POOL_SIZE_CLASS_COUNT]; +} ant_class_pool_t; + +typedef enum { + ANT_ALLOC_ROPE = 0, + ANT_ALLOC_SYMBOL = 1, + ANT_ALLOC_BIGINT = 2, + ANT_ALLOC_STRING = 3, +} ant_alloc_kind_t; + +typedef struct { + size_t used; + size_t capacity; + size_t blocks; +} ant_pool_stats_t; + +static inline ant_pool_block_t *pool_free_next(ant_pool_block_t *b) { + ant_pool_block_t *p; memcpy(&p, b->data, sizeof(ant_pool_block_t *)); return p; +} + +static inline void pool_free_set_next(ant_pool_block_t *b, ant_pool_block_t *n) { + memcpy(b->data, &n, sizeof(ant_pool_block_t *)); +} + +static inline void pool_block_free(ant_pool_block_t *block) { + ant_arena_free(block, sizeof(ant_pool_block_t) + block->cap); +} + +static inline ant_pool_block_t *pool_block_alloc(size_t cap) { + ant_pool_block_t *b = (ant_pool_block_t *)ant_os_alloc( + sizeof(ant_pool_block_t) + cap + ); + + if (!b) return NULL; + + b->next = NULL; + b->prev = NULL; + b->used = 0; + b->cap = cap; + + return b; +} + +static inline void pool_block_madvise_free(ant_pool_block_t *block) { +#ifndef _WIN32 + const uintptr_t page = 4096; + uintptr_t start = (uintptr_t)block->data + sizeof(void *); + uintptr_t aligned = (start + page - 1) & ~(page - 1); + uintptr_t end = ((uintptr_t)block->data + block->cap) & ~(page - 1); + if (aligned >= end) return; +#ifdef __APPLE__ + madvise((void *)aligned, end - aligned, MADV_FREE_REUSABLE); +#else + madvise((void *)aligned, end - aligned, MADV_DONTNEED); +#endif +#endif +} + +void js_pool_destroy(ant_pool_t *pool); +void js_class_pool_destroy(ant_class_pool_t *pool); + +void *js_type_alloc( + ant_t *js, ant_alloc_kind_t kind, + size_t size, size_t align +); + +void *pool_alloc_chain( + ant_pool_block_t **head, ant_pool_block_t **free_head, + size_t block_size, size_t size, size_t align +); + +ant_pool_stats_t js_pool_stats(ant_pool_t *pool); +ant_pool_stats_t js_class_pool_stats(ant_class_pool_t *pool); + +#endif diff --git a/include/roots.h b/include/roots.h deleted file mode 100644 index 5c9fec7..0000000 --- a/include/roots.h +++ /dev/null @@ -1,12 +0,0 @@ -#ifndef ROOTS_H -#define ROOTS_H - -#include "types.h" - -ant_handle_t js_root(ant_t *js, ant_value_t val); -ant_value_t js_deref(ant_t *js, ant_handle_t h); - -void js_unroot(ant_t *js, ant_handle_t h); -void js_root_update(ant_t *js, ant_handle_t h, ant_value_t val); - -#endif \ No newline at end of file diff --git a/include/shapes.h b/include/shapes.h new file mode 100644 index 0000000..6b5d669 --- /dev/null +++ b/include/shapes.h @@ -0,0 +1,75 @@ +#ifndef ANT_SHAPES_H +#define ANT_SHAPES_H + +#include "types.h" + +#include +#include + +#ifndef ANT_INOBJ_MAX_SLOTS +#define ANT_INOBJ_MAX_SLOTS 4u +#endif + +#define ANT_PROP_ATTR_WRITABLE (1u << 0) +#define ANT_PROP_ATTR_ENUMERABLE (1u << 1) +#define ANT_PROP_ATTR_CONFIGURABLE (1u << 2) +#define ANT_PROP_ATTR_DEFAULT (ANT_PROP_ATTR_WRITABLE | ANT_PROP_ATTR_ENUMERABLE | ANT_PROP_ATTR_CONFIGURABLE) + +typedef enum { + ANT_SHAPE_KEY_STRING = 0, + ANT_SHAPE_KEY_SYMBOL = 1, +} ant_shape_key_type_t; + +typedef struct { + ant_shape_key_type_t type; + union { + const char *interned; + ant_offset_t sym_off; + } key; + uint8_t attrs; + uint8_t has_getter; + uint8_t has_setter; + ant_value_t getter; + ant_value_t setter; +} ant_shape_prop_t; + +ant_shape_t *ant_shape_new(void); +ant_shape_t *ant_shape_new_with_inobj_limit(uint8_t inobj_limit); +ant_shape_t *ant_shape_clone(const ant_shape_t *shape); +bool ant_shape_is_shared(const ant_shape_t *shape); +void ant_shape_retain(ant_shape_t *shape); +void ant_shape_release(ant_shape_t *shape); +uint8_t ant_shape_get_inobj_limit(const ant_shape_t *shape); + +int32_t ant_shape_lookup_interned(const ant_shape_t *shape, const char *interned); +int32_t ant_shape_lookup_symbol(const ant_shape_t *shape, ant_offset_t sym_off); + +bool ant_shape_add_interned(ant_shape_t *shape, const char *interned, uint8_t attrs, uint32_t *out_slot); +bool ant_shape_add_symbol(ant_shape_t *shape, ant_offset_t sym_off, uint8_t attrs, uint32_t *out_slot); + +bool ant_shape_add_interned_tr(ant_shape_t **shape_pp, const char *interned, uint8_t attrs, uint32_t *out_slot); +bool ant_shape_add_symbol_tr(ant_shape_t **shape_pp, ant_offset_t sym_off, uint8_t attrs, uint32_t *out_slot); +bool ant_shape_remove_slot(ant_shape_t *shape, uint32_t slot, uint32_t *swapped_from); + +uint32_t ant_shape_count(const ant_shape_t *shape); +const ant_shape_prop_t *ant_shape_prop_at(const ant_shape_t *shape, uint32_t slot); +ant_shape_prop_t *ant_shape_prop_mut_at(ant_shape_t *shape, uint32_t slot); + +bool ant_shape_set_attrs_interned(ant_shape_t *shape, const char *interned, uint8_t attrs); +bool ant_shape_set_attrs_symbol(ant_shape_t *shape, ant_offset_t sym_off, uint8_t attrs); +uint8_t ant_shape_get_attrs(const ant_shape_t *shape, uint32_t slot); +bool ant_shape_clear_accessor_slot(ant_shape_t *shape, uint32_t slot); + +void ant_gc_shapes_begin(void); +void ant_gc_shapes_mark(ant_shape_t *shape); +void ant_gc_shapes_sweep(void); + +size_t ant_shape_total_bytes(void); +extern uint32_t ant_ic_epoch_counter; + +static inline void ant_ic_epoch_bump(void) { + ant_ic_epoch_counter++; + if (ant_ic_epoch_counter == 0) ant_ic_epoch_counter = 1; +} + +#endif diff --git a/include/silver/ast.h b/include/silver/ast.h index 79b5929..40f0ffc 100644 --- a/include/silver/ast.h +++ b/include/silver/ast.h @@ -87,19 +87,20 @@ typedef enum { } sv_var_kind_t; enum { - FN_ASYNC = 1 << 0, - FN_GENERATOR = 1 << 1, - FN_ARROW = 1 << 2, - FN_GETTER = 1 << 3, - FN_SETTER = 1 << 4, - FN_STATIC = 1 << 5, - FN_COMPUTED = 1 << 6, - FN_METHOD = 1 << 7, - FN_COLON = 1 << 8, - FN_PAREN = 1 << 9, - FN_USES_ARGS = 1 << 10, - FN_INVALID_COOKED = 1 << 11, - FN_PARSE_STRICT = 1 << 12, + FN_ASYNC = 1 << 0, + FN_GENERATOR = 1 << 1, + FN_ARROW = 1 << 2, + FN_GETTER = 1 << 3, + FN_SETTER = 1 << 4, + FN_STATIC = 1 << 5, + FN_COMPUTED = 1 << 6, + FN_METHOD = 1 << 7, + FN_COLON = 1 << 8, + FN_PAREN = 1 << 9, + FN_USES_ARGS = 1 << 10, + FN_INVALID_COOKED = 1 << 11, + FN_PARSE_STRICT = 1 << 12, + FN_TEMPLATE_SEGMENT = 1 << 13, }; enum { diff --git a/include/silver/engine.h b/include/silver/engine.h index 719aacc..4f61bb9 100644 --- a/include/silver/engine.h +++ b/include/silver/engine.h @@ -3,6 +3,7 @@ #include "silver/vm.h" #include "internal.h" +#include "runtime.h" #include "errors.h" #include @@ -55,9 +56,63 @@ typedef struct { uint8_t type; } sv_type_info_t; +typedef struct { + ant_shape_t *cached_shape; + ant_object_t *cached_holder; + uint32_t cached_index; + uint32_t epoch; + uintptr_t cached_aux; + ant_shape_t *add_from_shape; + ant_shape_t *add_to_shape; + uint32_t add_slot; + uint32_t add_epoch; + bool cached_is_own; +} sv_ic_entry_t; + +typedef struct { + uint32_t bc_off; + ant_shape_t *shared_shape; +} sv_obj_site_cache_t; + +#define SV_GF_IC_AUX_WARMUP_MASK ((uintptr_t)0xFFu) +#define SV_GF_IC_AUX_MISS_MASK ((uintptr_t)0xFF00u) +#define SV_GF_IC_AUX_ACTIVE_BIT ((uintptr_t)0x10000u) +#define SV_GF_IC_AUX_MISS_SHIFT 8u + +#define SV_GF_IC_WARMUP_ENABLE 16u +#define SV_GF_IC_MISS_DISABLE 4u + +static inline uint8_t sv_gf_ic_warmup(uintptr_t aux) { + return (uint8_t)(aux & SV_GF_IC_AUX_WARMUP_MASK); +} + +static inline uint8_t sv_gf_ic_miss_streak(uintptr_t aux) { + return (uint8_t)((aux & SV_GF_IC_AUX_MISS_MASK) >> SV_GF_IC_AUX_MISS_SHIFT); +} + +static inline bool sv_gf_ic_active(uintptr_t aux) { + return (aux & SV_GF_IC_AUX_ACTIVE_BIT) != 0; +} + +static inline uintptr_t sv_gf_ic_pack_aux(uint8_t warmup, uint8_t miss_streak, bool active) { + uintptr_t aux = ((uintptr_t)warmup & SV_GF_IC_AUX_WARMUP_MASK) | + ((uintptr_t)miss_streak << SV_GF_IC_AUX_MISS_SHIFT); + if (active) aux |= SV_GF_IC_AUX_ACTIVE_BIT; + return aux; +} + bool sv_lookup_srcpos(sv_func_t *func, int bc_offset, uint32_t *line, uint32_t *col); bool sv_lookup_srcspan(sv_func_t *func, int bc_offset, uint32_t *src_off, uint32_t *src_end); +#ifdef ANT_JIT +typedef struct { + uint16_t bc_off; + uint8_t miss_count; + uint8_t disabled; + sv_func_t *target; +} sv_call_target_fb_t; +#endif + struct sv_func { uint8_t *code; int code_len; @@ -65,8 +120,20 @@ struct sv_func { ant_value_t *constants; int const_count; + struct sv_func **child_funcs; + int child_func_count; + + uint32_t *gc_const_slots; + int gc_const_slot_count; + sv_atom_t *atoms; int atom_count; + + sv_ic_entry_t *ic_slots; + uint16_t ic_count; + + sv_obj_site_cache_t *obj_sites; + uint16_t obj_site_count; sv_upval_desc_t *upval_descs; int max_locals; @@ -84,7 +151,7 @@ struct sv_func { bool is_generator; bool is_method; bool is_tla; - uint32_t gc_epoch; + uint64_t gc_epoch; const char *name; const char *filename; @@ -105,9 +172,18 @@ struct sv_func { uint32_t back_edge_count; bool jit_compile_failed; + bool jit_compiling; uint32_t tfb_version; uint32_t jit_compiled_tfb_ver; uint8_t *type_feedback; + uint8_t *local_type_feedback; + uint64_t ctor_prop_samples; + uint64_t ctor_prop_hist[17]; + uint8_t ctor_inobj_limit; + uint8_t ctor_inobj_frozen; + + sv_call_target_fb_t *call_target_fb; + uint8_t call_target_fb_count; #endif }; @@ -162,27 +238,37 @@ struct sv_upvalue { ant_value_t *location; ant_value_t closed; struct sv_upvalue *next; - uint32_t gc_epoch; + uint64_t gc_epoch; }; +static inline sv_upvalue_t *js_upvalue_alloc(void) { + return (sv_upvalue_t *)fixed_arena_alloc(&rt->js->upvalue_arena); +} + #define SV_CALL_HAS_BOUND_ARGS (1u << 0) #define SV_CALL_HAS_SUPER (1u << 1) #define SV_CALL_IS_ARROW (1u << 2) #define SV_CALL_IS_DEFAULT_CTOR (1u << 3) typedef struct sv_closure { + uint32_t call_flags; + int bound_argc; sv_func_t *func; + sv_upvalue_t **upvalues; ant_value_t bound_this; ant_value_t *bound_argv; - int bound_argc; ant_value_t bound_args; ant_value_t super_val; ant_value_t func_obj; - uint32_t gc_epoch; - uint8_t call_flags; + + uint64_t gc_epoch; } sv_closure_t; +static inline sv_closure_t *js_closure_alloc(ant_t *js) { + return (sv_closure_t *)fixed_arena_alloc(&js->closure_arena); +} + static inline sv_closure_t *js_func_closure(ant_value_t func) { return (sv_closure_t *)(uintptr_t)vdata(func); } @@ -375,7 +461,7 @@ static inline ant_value_t *sv_prepend_bound_args( } static inline ant_value_t sv_call_resolve_bound(ant_t *js, sv_closure_t *closure, sv_call_ctx_t *ctx) { - uint8_t flags = closure->call_flags; + uint32_t flags = closure->call_flags; if (flags & SV_CALL_IS_ARROW) ctx->this_val = closure->bound_this; else if (vtype(closure->bound_this) != T_UNDEF) ctx->this_val = closure->bound_this; @@ -463,10 +549,19 @@ static inline ant_value_t sv_call_closure( #define SV_TFB_STR (1 << 1) #define SV_TFB_BOOL (1 << 2) #define SV_TFB_OTHER (1 << 3) +#define SV_TFB_CTOR_PROP_BINS 17 +#define SV_TFB_CTOR_PROP_OVERFLOW_FROM (SV_TFB_CTOR_PROP_BINS - 1) +#define SV_TFB_INOBJ_SLACK_ALLOCATIONS 32 +#define SV_TFB_INOBJ_P90_NUMERATOR 9 +#define SV_TFB_INOBJ_P90_DENOMINATOR 10 #define SV_JIT_THRESHOLD 100 #define SV_JIT_RECOMPILE_DELAY 50 #define SV_TFB_ALLOC_THRESHOLD 2 + +#define SV_CALL_FB_MAX_SLOTS 32 +#define SV_CALL_FB_MISS_DISABLE 4 + #define SV_JIT_RETRY_INTERP mkval(T_ERR, 1) #define SV_JIT_MAGIC 0xBA110ULL @@ -527,6 +622,152 @@ if (func->type_feedback) { static inline void sv_tfb_ensure(sv_func_t *fn) { if (!fn->type_feedback && fn->code_len > 0) fn->type_feedback = calloc((size_t)fn->code_len, 1); + if (!fn->local_type_feedback && fn->max_locals > 0) + fn->local_type_feedback = calloc((size_t)fn->max_locals, 1); +} + +static inline void sv_tfb_record_call_target(sv_func_t *func, int bc_off, sv_func_t *callee) { + if (!callee) return; + sv_call_target_fb_t *fb = func->call_target_fb; + int count = func->call_target_fb_count; + for (int i = 0; i < count; i++) { + if (fb[i].bc_off != (uint16_t)bc_off) continue; + if (fb[i].disabled) return; + if (fb[i].target == callee) return; + if (fb[i].target == NULL) { fb[i].target = callee; return; } + fb[i].miss_count++; + if (fb[i].miss_count >= SV_CALL_FB_MISS_DISABLE) { + fb[i].disabled = 1; + fb[i].target = NULL; + } else { + fb[i].target = callee; + } + func->tfb_version++; + return; + } + if (count >= SV_CALL_FB_MAX_SLOTS) return; + if (!fb) { + fb = calloc(SV_CALL_FB_MAX_SLOTS, sizeof(sv_call_target_fb_t)); + if (!fb) return; + func->call_target_fb = fb; + } + fb[count].bc_off = (uint16_t)bc_off; + fb[count].target = callee; + fb[count].miss_count = 0; + fb[count].disabled = 0; + func->call_target_fb_count = (uint8_t)(count + 1); +} + +static inline sv_func_t *sv_tfb_get_call_target(sv_func_t *func, int bc_off) { + sv_call_target_fb_t *fb = func->call_target_fb; + int count = func->call_target_fb_count; + for (int i = 0; i < count; i++) { + if (fb[i].bc_off == (uint16_t)bc_off && !fb[i].disabled) + return fb[i].target; + } + return NULL; +} + +static inline void sv_tfb_record_local(sv_func_t *func, int idx, ant_value_t v) { + if (func->local_type_feedback && idx >= 0 && idx < func->max_locals) { + uint8_t old = func->local_type_feedback[idx]; + uint8_t neu = old | sv_tfb_classify(v); + if (neu != old) { func->local_type_feedback[idx] = neu; func->tfb_version++; } + } +} + +static inline uint8_t sv_tfb_clamp_inobj_limit(uint32_t limit) { + return (limit > ANT_INOBJ_MAX_SLOTS) ? (uint8_t)ANT_INOBJ_MAX_SLOTS : (uint8_t)limit; +} + +static inline uint8_t sv_tfb_infer_inobj_limit(const sv_func_t *func, uint64_t samples) { + if (!func || samples == 0) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + + uint64_t target = ( + (samples * SV_TFB_INOBJ_P90_NUMERATOR) + + (SV_TFB_INOBJ_P90_DENOMINATOR - 1) + ) / SV_TFB_INOBJ_P90_DENOMINATOR; + if (target == 0) target = 1; + + uint64_t seen = 0; + for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) { + seen += func->ctor_prop_hist[i]; + if (seen < target) continue; + + if (i >= SV_TFB_CTOR_PROP_OVERFLOW_FROM) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + return sv_tfb_clamp_inobj_limit(i); + } + + return (uint8_t)ANT_INOBJ_MAX_SLOTS; +} + +static inline void sv_tfb_record_ctor_prop_count(ant_value_t ctor_func, ant_value_t instance) { + if (vtype(ctor_func) != T_FUNC) return; + if (!is_object_type(instance)) return; + sv_closure_t *closure = js_func_closure(ctor_func); + if (!closure || !closure->func) return; + ant_object_t *obj = js_obj_ptr(js_as_obj(instance)); + if (!obj) return; + + sv_func_t *func = closure->func; + uint32_t count = obj->prop_count; + uint32_t bin = (count < SV_TFB_CTOR_PROP_OVERFLOW_FROM) + ? count + : SV_TFB_CTOR_PROP_OVERFLOW_FROM; + func->ctor_prop_hist[bin]++; + uint64_t samples = ++func->ctor_prop_samples; + if (!func->ctor_inobj_frozen && samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) { + func->ctor_inobj_limit = sv_tfb_infer_inobj_limit(func, samples); + func->ctor_inobj_frozen = 1; + } +} + +static inline uint8_t sv_tfb_ctor_inobj_limit(ant_value_t ctor_func) { + if (vtype(ctor_func) != T_FUNC) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + sv_closure_t *closure = js_func_closure(ctor_func); + if (!closure || !closure->func) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + + sv_func_t *func = closure->func; + if (!func->ctor_inobj_frozen) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + return sv_tfb_clamp_inobj_limit(func->ctor_inobj_limit); +} + +static inline bool sv_tfb_ctor_inobj_limit_frozen(ant_value_t ctor_func) { + if (vtype(ctor_func) != T_FUNC) return false; + sv_closure_t *closure = js_func_closure(ctor_func); + if (!closure || !closure->func) return false; + return closure->func->ctor_inobj_frozen != 0; +} + +static inline uint32_t sv_tfb_ctor_inobj_slack_remaining(ant_value_t ctor_func) { + if (vtype(ctor_func) != T_FUNC) return SV_TFB_INOBJ_SLACK_ALLOCATIONS; + sv_closure_t *closure = js_func_closure(ctor_func); + if (!closure || !closure->func) return SV_TFB_INOBJ_SLACK_ALLOCATIONS; + sv_func_t *func = closure->func; + if (func->ctor_inobj_frozen || func->ctor_prop_samples >= SV_TFB_INOBJ_SLACK_ALLOCATIONS) return 0; + return (uint32_t)(SV_TFB_INOBJ_SLACK_ALLOCATIONS - func->ctor_prop_samples); +} +#endif + +#ifndef ANT_JIT +static inline void sv_tfb_record_ctor_prop_count(ant_value_t ctor_func, ant_value_t instance) { + (void)ctor_func; + (void)instance; +} + +static inline uint8_t sv_tfb_ctor_inobj_limit(ant_value_t ctor_func) { + (void)ctor_func; + return (uint8_t)ANT_INOBJ_MAX_SLOTS; +} + +static inline bool sv_tfb_ctor_inobj_limit_frozen(ant_value_t ctor_func) { + (void)ctor_func; + return false; +} + +static inline uint32_t sv_tfb_ctor_inobj_slack_remaining(ant_value_t ctor_func) { + (void)ctor_func; + return 0; } #endif @@ -571,7 +812,12 @@ static inline ant_value_t sv_vm_call( if (!is_construct_call) js->new_target = js_mkundef(); if (out_this) *out_this = this_val; - if (!is_construct_call && vtype(func) == T_OBJ && is_proxy(js, func)) + if (is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) + return js_proxy_construct(js, func, args, argc, sv_vm_get_new_target(vm, js)); + if (is_construct_call && !js_is_constructor(js, func)) + return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); + + if (!is_construct_call && vtype(func) == T_OBJ && is_proxy(func)) return js_proxy_apply(js, func, this_val, args, argc); if (vtype(func) == T_CFUNC) { diff --git a/include/silver/glue.h b/include/silver/glue.h index 7d327a6..25d9030 100644 --- a/include/silver/glue.h +++ b/include/silver/glue.h @@ -44,19 +44,46 @@ ant_value_t jit_helper_shr(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) ant_value_t jit_helper_ushr(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r); ant_value_t jit_helper_not(sv_vm_t *vm, ant_t *js, ant_value_t v); -ant_value_t jit_helper_get_global(ant_t *js, const char *str, uint32_t len); ant_value_t jit_helper_to_propkey(sv_vm_t *vm, ant_t *js, ant_value_t v); ant_value_t jit_helper_stack_overflow_error(sv_vm_t *vm, ant_t *js); -ant_value_t jit_helper_instanceof(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r); + ant_value_t jit_helper_delete(sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_value_t key); ant_value_t jit_helper_typeof(sv_vm_t *vm, ant_t *js, ant_value_t v); +ant_value_t jit_helper_get_global( + ant_t *js, const char *str, + sv_func_t *func, int32_t bc_off +); + +ant_value_t jit_helper_instanceof( + sv_vm_t *vm, ant_t *js, + ant_value_t l, ant_value_t r, + sv_func_t *func, int32_t bc_off +); + +ant_value_t jit_helper_call_is_proto( + sv_vm_t *vm, ant_t *js, + ant_value_t call_this, ant_value_t call_func, ant_value_t arg, + sv_func_t *func, int32_t bc_off +); + ant_value_t jit_helper_call( sv_vm_t *vm, ant_t *js, ant_value_t func, ant_value_t this_val, ant_value_t *args, int argc ); +ant_value_t jit_helper_apply( + sv_vm_t *vm, ant_t *js, + ant_value_t func, ant_value_t this_val, + ant_value_t *args, int argc +); + +ant_value_t jit_helper_rest( + sv_vm_t *vm, ant_t *js, + ant_value_t *args, int argc, int start +); + ant_value_t jit_helper_get_field( sv_vm_t *vm, ant_t *js, ant_value_t obj, const char *str, uint32_t len, diff --git a/include/silver/opcode.h b/include/silver/opcode.h index a9e55b9..43f608c 100644 --- a/include/silver/opcode.h +++ b/include/silver/opcode.h @@ -74,13 +74,13 @@ OP_DEF( PUT_UPVAL, 3, 1, 0, var_ref) OP_DEF( SET_UPVAL, 3, 1, 1, var_ref) OP_DEF( CLOSE_UPVAL, 3, 0, 0, loc) /* close upvalues >= loc */ -OP_DEF( GET_GLOBAL, 5, 0, 1, atom) /* push global[atom] */ -OP_DEF( GET_GLOBAL_UNDEF, 5, 0, 1, atom) /* push undefined if missing */ +OP_DEF( GET_GLOBAL, 7, 0, 1, atom) /* push global[atom] (atom + ic_idx:u16) */ +OP_DEF( GET_GLOBAL_UNDEF, 7, 0, 1, atom) /* push undefined if missing (atom + ic_idx:u16) */ OP_DEF( PUT_GLOBAL, 5, 1, 0, atom) /* global[atom] = TOS */ -OP_DEF( GET_FIELD, 5, 1, 1, atom) /* obj -> val */ -OP_DEF( GET_FIELD2, 5, 1, 2, atom) /* obj -> obj val */ -OP_DEF( PUT_FIELD, 5, 2, 0, atom) /* obj val -> */ +OP_DEF( GET_FIELD, 7, 1, 1, atom) /* obj -> val (atom + ic_idx:u16) */ +OP_DEF( GET_FIELD2, 7, 1, 2, atom) /* obj -> obj val (atom + ic_idx:u16) */ +OP_DEF( PUT_FIELD, 7, 2, 0, atom) /* obj val -> (atom + ic_idx:u16) */ OP_DEF( GET_ELEM, 1, 2, 1, none) /* obj key -> val */ OP_DEF( GET_ELEM2, 1, 2, 2, none) /* obj key -> obj val */ OP_DEF( PUT_ELEM, 1, 3, 0, none) /* obj key val -> */ @@ -126,7 +126,7 @@ OP_DEF( LT, 1, 2, 1, none) OP_DEF( LE, 1, 2, 1, none) OP_DEF( GT, 1, 2, 1, none) OP_DEF( GE, 1, 2, 1, none) -OP_DEF( INSTANCEOF, 1, 2, 1, none) +OP_DEF( INSTANCEOF, 3, 2, 1, u16) /* l r -> bool (ic_idx:u16) */ OP_DEF( IN, 1, 2, 1, none) OP_DEF( IS_NULLISH, 1, 1, 1, none) /* TOS is null|undefined? */ OP_DEF( IS_UNDEF_OR_NULL, 1, 1, 1, none) /* same, for ?? chains */ @@ -157,6 +157,7 @@ OP_DEF( JMP_TRUE8, 2, 1, 0, label8) /* short conditional */ OP_DEF( CALL, 3, 1, 1, npop) /* func args... -> result */ OP_DEF( CALL_METHOD, 3, 2, 1, npop) /* this func args... -> result */ +OP_DEF( CALL_IS_PROTO, 3, 3, 1, u16) /* this func arg -> bool (ic_idx:u16) */ OP_DEF( TAIL_CALL, 3, 1, 0, npop) /* tail-position call */ OP_DEF( TAIL_CALL_METHOD, 3, 2, 0, npop) OP_DEF( NEW, 3, 2, 1, npop) /* func new.target args -> obj */ diff --git a/include/silver/vm.h b/include/silver/vm.h index f1e06d2..e36a624 100644 --- a/include/silver/vm.h +++ b/include/silver/vm.h @@ -18,9 +18,6 @@ sv_vm_t *sv_vm_create(ant_t *js, sv_vm_kind_t kind); void sv_vm_destroy(sv_vm_t *vm); void sv_vm_limits(sv_vm_kind_t kind, int *out_stack_size, int *out_max_frames); -void sv_vm_gc_roots(sv_vm_t *vm, void (*op_val)(void *, ant_value_t *), void *ctx); -void sv_vm_gc_roots_pending(void (*op_val)(void *, ant_value_t *), void *ctx); - void sv_vm_visit_frame_funcs(sv_vm_t *vm, void (*visitor)(void *, sv_func_t *), void *ctx); void sv_disasm(ant_t *js, sv_func_t *func, const char *label); diff --git a/include/sugar.h b/include/sugar.h index 3553643..5def6ea 100644 --- a/include/sugar.h +++ b/include/sugar.h @@ -13,9 +13,9 @@ #define MCO_RESUME_SAVE(js_, mco_, res_) do { \ void *_saved_cstk = (js_)->cstk.base; \ size_t _saved_limit = (js_)->cstk.limit; \ - volatile char _gc_stk_mark; \ + volatile char _stk_mark; \ if (!mco_running()) \ - (js_)->cstk.main_lo = (void *)&_gc_stk_mark; \ + (js_)->cstk.main_lo = (void *)&_stk_mark; \ (res_) = mco_resume((mco_)); \ (js_)->cstk.base = _saved_cstk; \ (js_)->cstk.limit = _saved_limit; \ diff --git a/include/types.h b/include/types.h index f2b14ec..1ebbb36 100644 --- a/include/types.h +++ b/include/types.h @@ -4,13 +4,24 @@ #include #include -struct ant; - -typedef struct ant ant_t; typedef unsigned long long u64; +struct ant_object; +struct ant_shape; +struct ant_isolate_t; + +typedef struct ant_isolate_t ant_t; +typedef struct ant_pool_block ant_pool_block_t; + +typedef struct ant_object ant_object_t; +typedef struct ant_shape ant_shape_t; + +typedef struct sv_vm sv_vm_t; +typedef struct sv_func sv_func_t; +typedef struct sv_closure sv_closure_t; + typedef size_t ant_handle_t; typedef uint64_t ant_offset_t; typedef uint64_t ant_value_t; -#endif \ No newline at end of file +#endif diff --git a/libant/scripts/header.sh b/libant/scripts/header.sh index 3f1857f..8099848 100755 --- a/libant/scripts/header.sh +++ b/libant/scripts/header.sh @@ -20,8 +20,6 @@ HEADERS=( "debug.h:$INCLUDE_DIR/debug.h" "common.h:$INCLUDE_DIR/common.h" "types.h:$INCLUDE_DIR/types.h" - "gc.h:$INCLUDE_DIR/gc.h" - "roots.h:$INCLUDE_DIR/roots.h" "uthash.h:$VENDOR_DIR/uthash-2.3.0/src/uthash.h" "errors.h:$INCLUDE_DIR/errors.h" "ant.h:$INCLUDE_DIR/ant.h" diff --git a/maidfile.toml b/maidfile.toml index 4625683..d74fe6d 100644 --- a/maidfile.toml +++ b/maidfile.toml @@ -24,24 +24,19 @@ script = ["maid build -q", "./build/ant %{arg.1}"] script = "amber build .github/install/install.ab install.sh" [tasks.setup] -script = ['meson subprojects download', ''' - bash -c 'CC="ccache $(which clang)" \ - CC_LD="$(which ld64.lld)" \ - meson setup build --wipe' -'''] +script = ['meson subprojects download', 'meson setup build --wipe'] [tasks.no_jit] -script = ['meson subprojects download', ''' - bash -c 'CC="ccache $(which clang)" \ - CC_LD="$(which ld64.lld)" \ - meson setup build --wipe -Djit=false' -'''] +script = ['meson subprojects download', 'meson setup build --wipe -Djit=false'] [tasks.asan] -script = ['meson subprojects download', ''' - bash -c 'CC="ccache $(which clang)" \ - meson setup build --wipe -Db_sanitize=address -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false' -'''] +script = ['meson subprojects download', 'meson setup build --wipe -Db_sanitize=address -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false'] + +[tasks.debug] +script = [ + 'meson subprojects download', + 'meson setup build --wipe --buildtype=debug -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false -Dunity=off' +] [tasks.install] script = ["maid build -q", ''' @@ -55,9 +50,3 @@ script = ["maid build -q", ''' ln -sf ~/.ant/bin/ant ~/.ant/bin/antx fi' '''] - -[tasks.debug] -script = ['meson subprojects download', ''' - bash -c 'CC="ccache $(which clang)" \ - meson setup build --wipe --buildtype=debug -Doptimization=0 -Db_lto=false -Dstrip=false -Db_lundef=false -Dunity=off' -'''] diff --git a/meson/ant.version b/meson/ant.version index 7deb86f..8adc70f 100644 --- a/meson/ant.version +++ b/meson/ant.version @@ -1 +1 @@ -0.7.1 \ No newline at end of file +0.8.0 \ No newline at end of file diff --git a/meson/deps/meson.build b/meson/deps/meson.build index 754ebcc..2fecfe6 100644 --- a/meson/deps/meson.build +++ b/meson/deps/meson.build @@ -68,6 +68,15 @@ tlsuv_opts = cmake.subproject_options() tlsuv_opts.set_override_option('warning_level', '0') tlsuv_opts.append_compile_args('c', ['-w']) +zlib_ng_sub = subproject('zlib-ng', default_options: [ + 'b_lto=false', + 'warning_level=0', + 'zlib-compat=true' +]) +zlib_dep = zlib_ng_sub.get_variable('zlib_ng_dep') +zlib_ng_srcdir = subprojects_src / deps_info['zlib-ng']['dir'] +zlib_ng_builddir = subprojects_build / deps_info['zlib-ng']['dir'] + cmake_prefix = get_option('deps_prefix_cmake') tlsuv_cmake_defines = { 'BUILD_TESTING': 'OFF', @@ -78,6 +87,8 @@ tlsuv_cmake_defines = { 'CMAKE_POLICY_DEFAULT_CMP0179': 'NEW', 'llhttp_DIR': subprojects_src / deps_info['llhttp']['dir'] / 'cmake', 'libuv_DIR': subprojects_src / deps_info['libuv']['dir'] / 'cmake', + 'ZLIB_INCLUDE_DIR': zlib_ng_srcdir, + 'ZLIB_LIBRARY': zlib_ng_builddir / 'libz-ng.a', } if cmake_prefix != '' tlsuv_cmake_defines += {'MBEDTLS_DIR': cmake_prefix, 'CMAKE_PREFIX_PATH': cmake_prefix} @@ -132,11 +143,6 @@ lmdb_dep = subproject('lmdb', default_options: [ 'programs=disabled' ]).get_variable('lmdb_dep') -zlib_dep = subproject('zlib-ng', default_options: [ - 'b_lto=false', - 'warning_level=0' -]).get_variable('zlib_ng_dep') - nghttp2_dep = subproject('nghttp2', default_options: [ 'warning_level=0' ]).get_variable('libnghttp2_dep') diff --git a/shell.nix b/shell.nix new file mode 100644 index 0000000..4516598 --- /dev/null +++ b/shell.nix @@ -0,0 +1,10 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + packages = with pkgs; [ pkg-config libsodium libossp_uuid openssl.dev]; + + shellHook = '' + export CC="ccache clang" + export MACOSX_DEPLOYMENT_TARGET="15.0" + ''; +} \ No newline at end of file diff --git a/sources.json b/sources.json index 7bde261..b7ab1b7 100644 --- a/sources.json +++ b/sources.json @@ -1,10 +1,10 @@ { "engine": { - "patterns": ["src/*.c", "src/highlight/*.c", "src/esm/*.c", "src/cli/*.c", "src/modules/*.c", "src/silver/*.c"], + "patterns": ["src/*.c", "src/gc/*.c", "src/highlight/*.c", "src/esm/*.c", "src/cli/*.c", "src/modules/*.c", "src/silver/*.c"], "exclude": ["src/main.c", "src/watch.c"] }, "library": { - "patterns": ["src/*.c", "src/highlight/*.c", "src/esm/*.c", "src/modules/*.c", "src/silver/*.c"], + "patterns": ["src/*.c", "src/gc/*.c", "src/highlight/*.c", "src/esm/*.c", "src/modules/*.c", "src/silver/*.c"], "exclude": ["src/main.c", "src/watch.c"] }, "core": { diff --git a/src/ant.c b/src/ant.c index 1634a95..aa10780 100644 --- a/src/ant.c +++ b/src/ant.c @@ -5,19 +5,21 @@ #include // IWYU pragma: keep #include "ant.h" -#include "gc.h" #include "utf8.h" #include "debug.h" #include "tokens.h" #include "common.h" -#include "arena.h" #include "utils.h" #include "base64.h" #include "runtime.h" #include "internal.h" -#include "sugar.h" #include "errors.h" #include "descriptors.h" +#include "shapes.h" + +#include "gc.h" +#include "gc/objects.h" +#include "gc/roots.h" #include "esm/remote.h" #include "esm/loader.h" @@ -49,21 +51,13 @@ #include #endif -#include "modules/fs.h" #include "modules/bigint.h" #include "modules/timer.h" -#include "modules/fetch.h" #include "modules/symbol.h" #include "modules/ffi.h" -#include "modules/child_process.h" -#include "modules/readline.h" -#include "modules/process.h" #include "modules/date.h" #include "modules/buffer.h" #include "modules/collections.h" -#include "modules/navigator.h" -#include "modules/server.h" -#include "modules/events.h" #include "modules/lmdb.h" #include "modules/regex.h" @@ -101,9 +95,58 @@ typedef struct interned_string { struct interned_string *next; } interned_string_t; +typedef struct { + uint32_t id; + uint32_t flags; + const char *key; + uint32_t desc_len; + char desc[]; +} ant_symbol_heap_t; + static size_t intern_count = 0; static size_t intern_bytes = 0; -static interned_string_t *intern_buckets[ANT_LIMIT_SIZE_CACHE]; + +static interned_string_t **intern_buckets = NULL; +static size_t intern_bucket_count = 0; + +#define INTERN_BUCKET_MIN 1024u +#define INTERN_LOAD_NUM 4u +#define INTERN_LOAD_DEN 5u + +static bool intern_table_init(void) { + if (intern_buckets) return true; + intern_bucket_count = INTERN_BUCKET_MIN; + intern_buckets = ant_calloc(sizeof(*intern_buckets) * intern_bucket_count); + if (!intern_buckets) { + intern_bucket_count = 0; + return false; + } + return true; +} + +static bool intern_table_rehash(size_t new_bucket_count) { + if (!intern_buckets || new_bucket_count < INTERN_BUCKET_MIN) return false; + + interned_string_t **next = ant_calloc(sizeof(*next) * new_bucket_count); + if (!next) return false; + + for (size_t i = 0; i < intern_bucket_count; i++) { + interned_string_t *entry = intern_buckets[i]; + while (entry) { + interned_string_t *link = entry->next; + size_t bucket = (size_t)(entry->hash & (new_bucket_count - 1)); + entry->next = next[bucket]; + next[bucket] = entry; + entry = link; + } + } + + free(intern_buckets); + intern_buckets = next; + intern_bucket_count = new_bucket_count; + + return true; +} typedef struct promise_handler { ant_value_t onFulfilled; @@ -118,58 +161,24 @@ static const UT_icd promise_handler_icd = { .dtor = NULL, }; -typedef struct { - ant_offset_t obj_off; - const char *intern_ptr; - ant_offset_t prop_off; - ant_offset_t tail; - uint64_t generation; -} intern_prop_cache_entry_t; - -static uint64_t intern_prop_cache_gen = 1; -static intern_prop_cache_entry_t intern_prop_cache[ANT_LIMIT_SIZE_CACHE]; - -typedef struct promise_data_entry { - ant_value_t value; - UT_array *handlers; - uint32_t promise_id; - uint32_t trigger_pid; - ant_offset_t obj_offset; - int state; - bool has_rejection_handler; - bool processing; - UT_hash_handle hh; - UT_hash_handle hh_unhandled; -} promise_data_entry_t; - -static promise_data_entry_t *promise_registry = NULL; -static promise_data_entry_t *unhandled_rejections = NULL; static uint32_t next_promise_id = 1; - -static promise_data_entry_t *get_promise_data(uint32_t promise_id, bool create); static uint32_t get_promise_id(ant_t *js, ant_value_t p); -static bool js_try_grow_memory(ant_t *js, size_t needed); -typedef struct proxy_data { - ant_offset_t obj_offset; - ant_value_t target; - ant_value_t handler; - bool revoked; - UT_hash_handle hh; -} proxy_data_t; - -typedef struct dynamic_accessors { - ant_offset_t obj_offset; - js_getter_fn getter; - js_setter_fn setter; - js_deleter_fn deleter; - js_keys_fn keys; - UT_hash_handle hh; -} dynamic_accessors_t; +static ant_promise_state_t *get_promise_data(ant_t *js, ant_value_t promise, bool create); +static ant_proxy_state_t *get_proxy_data(ant_value_t obj); -static proxy_data_t *proxy_registry = NULL; -static proxy_data_t *get_proxy_data(ant_value_t obj); -static dynamic_accessors_t *accessor_registry = NULL; +typedef struct { + bool has_getter; + bool has_setter; + bool writable; + bool enumerable; + bool configurable; + ant_value_t getter; + ant_value_t setter; +} prop_meta_t; + +static bool lookup_symbol_prop_meta(ant_value_t cur_obj, ant_offset_t sym_off, prop_meta_t *out); +static bool lookup_string_prop_meta(ant_t *js, ant_value_t cur_obj, const char *key, size_t klen, prop_meta_t *out); ant_value_t tov(double d) { union { double d; ant_value_t v; } u = {d}; @@ -191,23 +200,230 @@ size_t vdata(ant_value_t v) { return (size_t)(v & NANBOX_DATA_MASK); } -static ant_value_t get_slot(ant_t *js, ant_value_t obj, internal_slot_t slot); -static void set_slot(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t value); +ant_object_t *js_obj_ptr(ant_value_t v) { + if (!is_object_type(v)) return NULL; + ant_value_t as_obj = js_as_obj(v); + return (ant_object_t *)(uintptr_t)vdata(as_obj); +} + +void js_mark_constructor(ant_value_t value, bool is_constructor) { + ant_object_t *obj = js_obj_ptr(value); + if (obj) obj->is_constructor = is_constructor ? 1u : 0u; +} -static ant_value_t get_proto(ant_t *js, ant_value_t obj); -static void set_proto(ant_t *js, ant_value_t obj, ant_value_t proto); +static inline ant_flat_string_t *str_flat_ptr(ant_value_t value) { + if (vtype(value) != T_STR || str_is_heap_rope(value)) return NULL; + return (ant_flat_string_t *)(uintptr_t)vdata(value); +} + +static inline ant_rope_heap_t *str_rope_ptr(ant_value_t value) { + return (ant_rope_heap_t *)(uintptr_t)(vdata(value) & ~1ULL); +} + +static inline ant_value_t mkrope_value(ant_rope_heap_t *rope) { + return mkval(T_STR, ((uintptr_t)rope) | 1ULL); +} + +static inline ant_extra_slot_t *obj_extra_slots(ant_object_t *obj) { + return (ant_extra_slot_t *)obj->extra_slots; +} + +static ant_value_t obj_extra_get(ant_object_t *obj, internal_slot_t slot) { + if (!obj || obj->extra_count == 0) return js_mkundef(); + ant_extra_slot_t *entries = obj_extra_slots(obj); + for (uint8_t i = 0; i < obj->extra_count; i++) { + if ((internal_slot_t)entries[i].slot == slot) return entries[i].value; + } + return js_mkundef(); +} + +static bool obj_extra_set(ant_object_t *obj, internal_slot_t slot, ant_value_t value) { + if (!obj) return false; + ant_extra_slot_t *entries = obj_extra_slots(obj); + for (uint8_t i = 0; i < obj->extra_count; i++) { + if ((internal_slot_t)entries[i].slot == slot) { + entries[i].value = value; + return true; + } + } + + if (obj->extra_count == UINT8_MAX) return false; + uint8_t next_count = (uint8_t)(obj->extra_count + 1); + ant_extra_slot_t *next = realloc(entries, sizeof(*next) * next_count); + if (!next) return false; + + next[obj->extra_count].slot = (uint8_t)slot; + next[obj->extra_count].value = value; + obj->extra_slots = (ant_value_t *)next; + obj->extra_count = next_count; + return true; +} + +static ant_offset_t propref_make(ant_t *js, ant_object_t *obj, uint32_t slot) { + if (!js || !obj) return 0; + + if (js->prop_refs_len >= js->prop_refs_cap) { + ant_offset_t next_cap = js->prop_refs_cap ? js->prop_refs_cap * 2 : 256; + ant_prop_ref_t *next = realloc(js->prop_refs, sizeof(*next) * next_cap); + if (!next) return 0; + js->prop_refs = next; + js->prop_refs_cap = next_cap; + } + + ant_offset_t handle = js->prop_refs_len + 1; + js->prop_refs[js->prop_refs_len++] = (ant_prop_ref_t){ + .obj = obj, + .slot = slot, + .valid = true, + }; + obj->propref_count++; + return handle; +} + +static ant_prop_ref_t *propref_get(ant_t *js, ant_offset_t handle) { + if (!js || handle == 0 || handle > js->prop_refs_len) return NULL; + ant_prop_ref_t *ref = &js->prop_refs[handle - 1]; + return ref->valid ? ref : NULL; +} + +static inline ant_value_t propref_load(ant_t *js, ant_offset_t handle) { + ant_prop_ref_t *ref = propref_get(js, handle); + if (!ref || !ref->obj || ref->slot >= ref->obj->prop_count) return js_mkundef(); + return ant_object_prop_get_unchecked(ref->obj, ref->slot); +} + +static inline bool propref_store(ant_t *js, ant_offset_t handle, ant_value_t value) { + ant_prop_ref_t *ref = propref_get(js, handle); + if (!ref || !ref->obj || ref->slot >= ref->obj->prop_count) return false; + ant_object_prop_set_unchecked(ref->obj, ref->slot, value); + gc_write_barrier(js, ref->obj, value); + return true; +} + +static void propref_adjust_after_swap_delete(ant_t *js, ant_object_t *obj, uint32_t deleted_slot, uint32_t swapped_from) { + if (!js || !obj || obj->propref_count == 0) return; + + for (ant_offset_t i = js->prop_refs_len; i-- > 0;) { + ant_prop_ref_t *ref = &js->prop_refs[i]; + if (!ref->valid || ref->obj != obj) continue; + + if (ref->slot == deleted_slot) { + ref->valid = false; + obj->propref_count--; + if (obj->propref_count == 0) return; + } else if (ref->slot == swapped_from) ref->slot = deleted_slot; + } +} + +bool js_obj_ensure_prop_capacity(ant_object_t *obj, uint32_t needed) { + if (!obj) return false; + uint32_t inobj_limit = ant_object_inobj_limit(obj); + uint32_t old_count = obj->prop_count; + if (needed <= obj->prop_count) return true; + + if (needed > inobj_limit) { + uint32_t overflow_needed = needed - inobj_limit; + if (overflow_needed > obj->overflow_cap) { + uint32_t new_cap = obj->overflow_cap ? (uint32_t)obj->overflow_cap * 2 : 4; + while (new_cap < overflow_needed) new_cap *= 2; + + if (new_cap > 255) new_cap = overflow_needed; + ant_value_t *next = realloc(obj->overflow_prop, sizeof(*next) * new_cap); + if (!next) return false; + + obj->overflow_prop = next; + obj->overflow_cap = (uint8_t)new_cap; + } + } else if (obj->overflow_prop) { + free(obj->overflow_prop); + obj->overflow_prop = NULL; + obj->overflow_cap = 0; + } + + obj->prop_count = needed; + for (uint32_t i = old_count; i < needed; i++) { + ant_object_prop_set_unchecked(obj, i, js_mkundef()); + } + return true; +} + +bool js_obj_ensure_unique_shape(ant_object_t *obj) { + if (!obj || !obj->shape) return false; + if (!ant_shape_is_shared(obj->shape)) return true; -static inline ant_offset_t offtolen(ant_offset_t off) { return (off >> 3) - 1; } -static inline ant_offset_t align64(ant_offset_t v) { return (v + 7) & ~7ULL; } + ant_shape_t *copy = ant_shape_clone(obj->shape); + if (!copy) return false; -static void saveoff(ant_t *js, ant_offset_t off, ant_offset_t val) { - memcpy(&js->mem[off], &val, sizeof(val)); + ant_shape_release(obj->shape); + obj->shape = copy; + return true; } -static void saveval(ant_t *js, ant_offset_t off, ant_value_t val) { - memcpy(&js->mem[off], &val, sizeof(val)); +static void obj_remove_prop_slot(ant_object_t *obj, uint32_t slot) { + if (!obj || slot >= obj->prop_count) return; + uint32_t last = obj->prop_count - 1; + if (slot != last) { + ant_object_prop_set_unchecked(obj, slot, ant_object_prop_get_unchecked(obj, last)); + } + obj->prop_count--; +} + +static ant_exotic_ops_t *obj_ensure_exotic_ops(ant_object_t *obj) { + if (!obj) return NULL; + if (!obj->exotic_ops) { + ant_exotic_ops_t *ops = calloc(1, sizeof(*ops)); + if (!ops) return NULL; + obj->exotic_ops = ops; + } + return (ant_exotic_ops_t *)(void *)obj->exotic_ops; +} + +static ant_object_t *obj_alloc(ant_t *js, uint8_t type_tag, uint8_t inobj_limit) { + size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); + if (threshold < 2048) threshold = 2048; + if (js->obj_arena.live_count >= threshold) gc_run(js); + + ant_object_t *obj = (ant_object_t *)fixed_arena_alloc(&js->obj_arena); + if (!obj) return NULL; + + obj->type_tag = type_tag; + obj->proto = js_mkundef(); + obj->shape = ant_shape_new_with_inobj_limit(inobj_limit); + obj->overflow_prop = NULL; + obj->overflow_cap = 0; + obj->prop_count = 0; + obj->inobj_limit = ant_shape_get_inobj_limit(obj->shape); + for (uint32_t i = 0; i < ANT_INOBJ_MAX_SLOTS; i++) obj->inobj[i] = js_mkundef(); + obj->extensible = 1; + obj->frozen = 0; + obj->sealed = 0; + obj->is_exotic = 0; + obj->is_constructor = 0; + obj->fast_array = 0; + obj->exotic_ops = NULL; + obj->exotic_keys = NULL; + obj->promise_state = NULL; + obj->proxy_state = NULL; + obj->u.data.value = js_mkundef(); + obj->extra_slots = NULL; + obj->extra_count = 0; + obj->gc_pending_next = NULL; + obj->gc_pending_rooted = false; + obj->generation = 0; + obj->in_remember_set = 0; + + obj->next = js->objects; + js->objects = obj; + + return obj; } +static ant_value_t get_slot(ant_value_t obj, internal_slot_t slot); +static void set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value); + +static ant_value_t get_proto(ant_t *js, ant_value_t obj); +static void set_proto(ant_t *js, ant_value_t obj, ant_value_t proto); + const char *typestr(uint8_t t) { static const char *names[] = { [T_UNDEF] = "undefined", [T_NULL] = "object", [T_BOOL] = "boolean", @@ -215,7 +431,7 @@ const char *typestr(uint8_t t) { [T_SYMBOL] = "symbol", [T_OBJ] = "object", [T_ARR] = "object", [T_FUNC] = "function", [T_CFUNC] = "function", [T_CLOSURE] = "closure", [T_PROMISE] = "promise", [T_GENERATOR] = "generator", - [T_PROP] = "prop", [T_ERR] = "err", [T_TYPEDARRAY] = "typedarray", + [T_ERR] = "err", [T_TYPEDARRAY] = "typedarray", [T_FFI] = "ffi", [T_NTARG] = "ntarg" }; @@ -233,12 +449,29 @@ ant_value_t mkval(uint8_t type, uint64_t data) { } ant_value_t js_obj_to_func_ex(ant_value_t obj, uint8_t flags) { - sv_closure_t *closure = calloc(1, sizeof(sv_closure_t)); + sv_closure_t *closure = js_closure_alloc(rt->js); + if (!closure) return mkval(T_ERR, 0); closure->func_obj = (vtype(obj) == T_OBJ) ? obj : mkval(T_OBJ, vdata(obj)); closure->bound_this = js_mkundef(); closure->bound_args = js_mkundef(); closure->super_val = js_mkundef(); closure->call_flags = flags; + ant_object_t *func_obj = js_obj_ptr(closure->func_obj); + if (func_obj) { + if (flags & SV_CALL_IS_DEFAULT_CTOR) { + func_obj->is_constructor = 1; + } else if ( + !func_obj->is_constructor && + func_obj->shape && + INTERN_PROTOTYPE && + vtype(obj_extra_get(func_obj, SLOT_CFUNC)) == T_CFUNC + ) { + // mark native function objects as constructors when they are + // created with an explicit .prototype own property. + if (ant_shape_lookup_interned(func_obj->shape, INTERN_PROTOTYPE) >= 0) + func_obj->is_constructor = 1; + } + } return mkval(T_FUNC, (uintptr_t)closure); } @@ -255,12 +488,8 @@ void *js_gettypedarray(ant_value_t val) { return (void *)vdata(val); } -ant_value_t js_get_slot(ant_t *js, ant_value_t obj, internal_slot_t slot) { - return get_slot(js, js_as_obj(obj), slot); -} - -void js_set_slot(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t value) { - set_slot(js, js_as_obj(obj), slot, value); +ant_value_t js_get_slot(ant_value_t obj, internal_slot_t slot) { + return get_slot(js_as_obj(obj), slot); } ant_value_t js_mkffi(unsigned int index) { @@ -277,14 +506,10 @@ typedef enum { NTARG_NEW_TARGET = 1 } ntarg_kind_t; -inline size_t js_getbrk(ant_t *js) { - return (size_t) js->brk; -} - static inline bool is_unboxed_obj(ant_t *js, ant_value_t val, ant_value_t expected_proto) { if (vtype(val) != T_OBJ) return false; - if (vtype(get_slot(js, val, SLOT_PRIMITIVE)) != T_UNDEF) return false; - ant_value_t proto = get_slot(js, val, SLOT_PROTO); + if (vtype(get_slot(val, SLOT_PRIMITIVE)) != T_UNDEF) return false; + ant_value_t proto = get_slot(val, SLOT_PROTO); return vdata(proto) == vdata(expected_proto); } @@ -307,20 +532,20 @@ static size_t strstring(ant_t *js, ant_value_t value, char *buf, size_t len); static size_t strkey(ant_t *js, ant_value_t value, char *buf, size_t len); ant_offset_t vstrlen(ant_t *js, ant_value_t v) { - ant_offset_t off = (ant_offset_t) vdata(v); - ant_offset_t header = loadoff(js, off); - if (header & ROPE_FLAG) { - return offtolen(header & ~(ROPE_FLAG | (ROPE_DEPTH_MASK << ROPE_DEPTH_SHIFT))); + if (str_is_heap_rope(v)) { + ant_rope_heap_t *rope = str_rope_ptr(v); + return rope ? rope->len : 0; } - return offtolen(header); + ant_flat_string_t *flat = str_flat_ptr(v); + return flat ? flat->len : 0; } static ant_value_t proxy_read_target(ant_t *js, ant_value_t obj); static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj); static ant_value_t proxy_aware_get_elem(ant_t *js, ant_value_t obj, const char *key, size_t key_len); -static ant_offset_t get_dense_buf(ant_t *js, ant_value_t arr); -static ant_offset_t dense_length(ant_t *js, ant_offset_t doff); +static ant_offset_t get_dense_buf(ant_value_t arr); +static ant_offset_t dense_capacity(ant_offset_t doff); static ant_offset_t get_array_length(ant_t *js, ant_value_t arr); static ant_value_t arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx); static bool arr_has(ant_t *js, ant_value_t arr, ant_offset_t idx); @@ -328,10 +553,12 @@ static bool arr_has(ant_t *js, ant_value_t arr, ant_offset_t idx); static bool streq(const char *buf, size_t len, const char *p, size_t n); static bool parse_func_params(ant_t *js, uint8_t *flags, int *out_count); static bool try_dynamic_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value); -static ant_offset_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out); +static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out); static ant_value_t call_proto_accessor(ant_t *js, ant_value_t prim, ant_value_t accessor, bool has_accessor, ant_value_t *arg, int arg_count, bool is_setter); static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type); +static inline ant_value_t lkp_val(ant_t *js, ant_value_t obj, const char *buf, size_t len); +static inline ant_value_t lkp_sym_proto_val(ant_t *js, ant_value_t obj, ant_offset_t sym_off); static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len); static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len); static ant_value_t js_call_valueOf(ant_t *js, ant_value_t value); @@ -355,7 +582,9 @@ static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t ke static ant_value_t get_prototype_for_type(ant_t *js, uint8_t type); static ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len); -static ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len); +ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len); +static ant_offset_t get_array_length(ant_t *js, ant_value_t arr); +static inline void array_len_set(ant_t *js, ant_value_t obj, ant_offset_t new_len); typedef struct { ant_value_t handle; bool is_new; } ctor_t; @@ -370,7 +599,7 @@ static ctor_t get_constructor(ant_t *js, const char *name, size_t len) { ant_value_t unwrap_primitive(ant_t *js, ant_value_t val) { if (__builtin_expect(vtype(val) != T_OBJ, 1)) return val; - ant_value_t prim = get_slot(js, val, SLOT_PRIMITIVE); + ant_value_t prim = get_slot(val, SLOT_PRIMITIVE); if (__builtin_expect(vtype(prim) == T_UNDEF, 1)) return val; return prim; } @@ -379,7 +608,7 @@ static ant_value_t to_string_val(ant_t *js, ant_value_t val) { uint8_t t = vtype(val); if (t == T_STR) return val; if (t == T_OBJ) { - ant_value_t prim = get_slot(js, val, SLOT_PRIMITIVE); + ant_value_t prim = get_slot(val, SLOT_PRIMITIVE); if (vtype(prim) == T_STR) return prim; } return js_call_toString(js, val); @@ -489,18 +718,15 @@ static void scan_obj_refs(ant_t *js, ant_value_t obj) { if (stringify_depth >= MAX_STRINGIFY_DEPTH) return; stringify_stack[stringify_depth++] = obj; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(js_as_obj(obj))) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (!is_slot_prop(header)) { - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - scan_refs(js, val); + ant_object_t *ptr = js_obj_ptr(obj); + if (ptr && ptr->shape) { + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) { + scan_refs(js, ant_object_prop_get_unchecked(ptr, i)); } - next = next_prop(header); } - - ant_value_t proto_val = get_slot(js, obj, SLOT_PROTO); + + ant_value_t proto_val = get_proto(js, obj); if (vtype(proto_val) == T_OBJ) scan_refs(js, proto_val); stringify_depth--; @@ -515,15 +741,12 @@ static void scan_arr_refs(ant_t *js, ant_value_t obj) { if (stringify_depth >= MAX_STRINGIFY_DEPTH) return; stringify_stack[stringify_depth++] = obj; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(js_as_obj(obj))) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (!is_slot_prop(header)) { - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - scan_refs(js, val); + ant_object_t *ptr = js_obj_ptr(obj); + if (ptr && ptr->shape) { + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) { + scan_refs(js, ant_object_prop_get_unchecked(ptr, i)); } - next = next_prop(header); } stringify_depth--; @@ -540,15 +763,12 @@ static void scan_func_refs(ant_t *js, ant_value_t value) { if (stringify_depth >= MAX_STRINGIFY_DEPTH) return; stringify_stack[stringify_depth++] = func_obj; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(func_obj)) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (!is_slot_prop(header)) { - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - scan_refs(js, val); + ant_object_t *ptr = js_obj_ptr(func_obj); + if (ptr && ptr->shape) { + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count && i < ptr->prop_count; i++) { + scan_refs(js, ant_object_prop_get_unchecked(ptr, i)); } - next = next_prop(header); } stringify_depth--; @@ -598,32 +818,10 @@ static size_t add_indent(char *buf, size_t len, int level) { return wanted; } -static inline ant_offset_t get_prop_koff(ant_t *js, ant_offset_t prop) { - return loadoff(js, prop + (ant_offset_t) sizeof(prop)); -} - -static inline bool is_sym_key_prop(ant_t *js, ant_offset_t prop) { - ant_offset_t koff = get_prop_koff(js, prop); - return koff < js->brk && (loadoff(js, koff) & 0xF) == 0; -} - -static void get_prop_key(ant_t *js, ant_offset_t prop, const char **key, ant_offset_t *klen) { - ant_offset_t koff = get_prop_koff(js, prop); - *klen = offtolen(loadoff(js, koff)); - *key = (char *) &js->mem[koff + sizeof(koff)]; -} - -static ant_value_t get_prop_val(ant_t *js, ant_offset_t prop) { - ant_offset_t koff = get_prop_koff(js, prop); - return loadval(js, prop + (ant_offset_t) (sizeof(prop) + sizeof(koff))); -} - const char *get_str_prop(ant_t *js, ant_value_t obj, const char *key, ant_offset_t klen, ant_offset_t *out_len) { - ant_offset_t off = lkp(js, obj, key, klen); - if (off <= 0) return NULL; - ant_value_t v = resolveprop(js, mkval(T_PROP, off)); + ant_value_t v = lkp_val(js, obj, key, klen); if (vtype(v) != T_STR) return NULL; - return (const char *)&js->mem[vstr(js, v, out_len)]; + return (const char *)(uintptr_t)(vstr(js, v, out_len)); } static bool is_small_array(ant_t *js, ant_value_t obj, int *elem_count) { @@ -663,37 +861,53 @@ static inline bool parse_array_index(const char *key, size_t klen, ant_offset_t return true; } -static ant_offset_t get_array_length(ant_t *js, ant_value_t arr) { - ant_offset_t doff = get_dense_buf(js, arr); - ant_offset_t dense_len = doff ? dense_length(js, doff) : 0; - ant_offset_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); - if (off) { - ant_value_t val = resolveprop(js, mkval(T_PROP, off)); - if (vtype(val) == T_NUM) { - ant_offset_t prop_len = (ant_offset_t) tod(val); - return prop_len > dense_len ? prop_len : dense_len; - } +#define ANT_ARRAY_INDEX_EXCLUSIVE ((ant_offset_t)UINT32_MAX) + +static inline ant_object_t *array_obj_ptr(ant_value_t obj) { + if (!is_object_type(obj)) return NULL; + ant_object_t *ptr = js_obj_ptr(obj); + return (ptr && ptr->type_tag == T_ARR) ? ptr : NULL; +} + +static inline void array_define_or_set_index(ant_t *js, ant_value_t obj, const char *key, size_t klen) { + if (!key) return; + if (!array_obj_ptr(obj)) return; + + unsigned long idx = 0; + if (!parse_array_index(key, klen, ANT_ARRAY_INDEX_EXCLUSIVE, &idx)) return; + + ant_offset_t next_len = (ant_offset_t)idx + 1; + if (next_len > get_array_length(js, obj)) { + array_len_set(js, obj, next_len); } - return dense_len; +} + +static ant_offset_t get_array_length(ant_t *js, ant_value_t arr) { + if (!is_object_type(arr)) return 0; + + ant_object_t *arr_ptr = array_obj_ptr(arr); + if (arr_ptr) return (ant_offset_t)arr_ptr->u.array.len; + + ant_value_t val = lkp_interned_val(js, arr, INTERN_LENGTH); + if (vtype(val) == T_NUM) return (ant_offset_t) tod(val); + + return 0; } static ant_value_t get_obj_ctor(ant_t *js, ant_value_t obj) { - ant_value_t ctor = get_slot(js, obj, SLOT_CTOR); + ant_value_t ctor = get_slot(obj, SLOT_CTOR); if (vtype(ctor) == T_FUNC) return ctor; - ant_value_t proto = get_slot(js, obj, SLOT_PROTO); + ant_value_t proto = get_slot(obj, SLOT_PROTO); if (vtype(proto) != T_OBJ) return js_mkundef(); - ant_offset_t off = lkp_interned(js, proto, INTERN_CONSTRUCTOR, 11); - return off ? resolveprop(js, mkval(T_PROP, off)) : js_mkundef(); + return lkp_interned_val(js, proto, INTERN_CONSTRUCTOR); } static const char *get_func_name(ant_t *js, ant_value_t func, ant_offset_t *out_len) { if (vtype(func) != T_FUNC) return NULL; - ant_offset_t off = lkp(js, js_func_obj(func), "name", 4); - if (!off) return NULL; - ant_value_t name = resolveprop(js, mkval(T_PROP, off)); + ant_value_t name = lkp_val(js, js_func_obj(func), "name", 4); if (vtype(name) != T_STR) return NULL; ant_offset_t str_off = vstr(js, name, out_len); - return (const char *) &js->mem[str_off]; + return (const char *)(uintptr_t)(str_off); } static const char *get_class_name(ant_t *js, ant_value_t obj, ant_offset_t *out_len, const char *skip) { @@ -704,8 +918,11 @@ static const char *get_class_name(ant_t *js, ant_value_t obj, ant_offset_t *out_ } static inline ant_offset_t dense_iterable_length(ant_t *js, ant_value_t obj) { - ant_offset_t doff = get_dense_buf(js, obj); - return doff ? dense_length(js, doff) : 0; + ant_offset_t doff = get_dense_buf(obj); + if (!doff) return 0; + ant_offset_t dense_len = dense_capacity(doff); + ant_offset_t semantic_len = get_array_length(js, obj); + return dense_len < semantic_len ? dense_len : semantic_len; } static size_t strarr(ant_t *js, ant_value_t obj, char *buf, size_t len) { @@ -713,7 +930,7 @@ static size_t strarr(ant_t *js, ant_value_t obj, char *buf, size_t len) { if (ref) return ref > 0 ? (size_t) snprintf(buf, len, "[Circular *%d]", ref) : cpy(buf, len, "[Circular]", 10); push_stringify(obj); - ant_offset_t first = loadoff(js, (ant_offset_t) vdata(js_as_obj(obj))) & ~(3U | FLAGMASK); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); ant_offset_t length = get_array_length(js, obj); ant_offset_t d_len = dense_iterable_length(js, obj); ant_offset_t iter_len = (d_len >= length) ? length : d_len; @@ -749,25 +966,28 @@ static size_t strarr(ant_t *js, ant_value_t obj, char *buf, size_t len) { printed_first = true; } - for (ant_offset_t p = first; p < js->brk && p != 0; p = next_prop(loadoff(js, p))) { - ant_offset_t header = loadoff(js, p); - if (is_slot_prop(header)) continue; - - const char *key; ant_offset_t klen; - get_prop_key(js, p, &key, &klen); - if (streq(key, klen, "length", 6)) continue; - - if (printed_first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); - - if (is_array_index(key, klen)) { - n += tostr(js, get_prop_val(js, p), buf + n, REMAIN(n, len)); - } else { - n += cpy(buf + n, REMAIN(n, len), key, klen); - n += cpy(buf + n, REMAIN(n, len), ": ", 2); - n += tostr(js, get_prop_val(js, p), buf + n, REMAIN(n, len)); + if (ptr && ptr->shape) { + uint32_t shape_count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < shape_count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue; + const char *key = prop->key.interned; + ant_offset_t klen = (ant_offset_t)strlen(key); + if (is_length_key(key, klen)) continue; + + if (printed_first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); + if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); + + ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); + if (is_array_index(key, klen)) { + n += tostr(js, val, buf + n, REMAIN(n, len)); + } else { + n += cpy(buf + n, REMAIN(n, len), key, klen); + n += cpy(buf + n, REMAIN(n, len), ": ", 2); + n += tostr(js, val, buf + n, REMAIN(n, len)); + } + printed_first = true; } - printed_first = true; } if (!inline_mode) { @@ -782,7 +1002,7 @@ static size_t strarr(ant_t *js, ant_value_t obj, char *buf, size_t len) { } static size_t strdate(ant_t *js, ant_value_t obj, char *buf, size_t len) { - ant_value_t time_val = js_get_slot(js, obj, SLOT_DATA); + ant_value_t time_val = js_get_slot(obj, SLOT_DATA); if (vtype(time_val) != T_NUM) return cpy(buf, len, "Invalid Date", 12); static const date_string_spec_t kSpec = {DATE_STRING_FMT_ISO, DATE_STRING_PART_ALL}; @@ -792,7 +1012,7 @@ static size_t strdate(ant_t *js, ant_value_t obj, char *buf, size_t len) { ant_offset_t slen; ant_offset_t soff = vstr(js, iso, &slen); - return cpy(buf, len, (const char *)&js->mem[soff], slen); + return cpy(buf, len, (const char *)(uintptr_t)(soff), slen); } static bool is_valid_identifier(const char *str, ant_offset_t slen) { @@ -808,7 +1028,7 @@ static bool is_valid_identifier(const char *str, ant_offset_t slen) { static size_t strkey(ant_t *js, ant_value_t value, char *buf, size_t len) { ant_offset_t slen, off = vstr(js, value, &slen); - const char *str = (const char *) &js->mem[off]; + const char *str = (const char *)(uintptr_t)(off); if (is_valid_identifier(str, slen)) { return cpy(buf, len, str, slen); @@ -816,43 +1036,49 @@ static size_t strkey(ant_t *js, ant_value_t value, char *buf, size_t len) { return strstring(js, value, buf, len); } +static size_t strkey_interned(ant_t *js, const char *key, size_t klen, char *buf, size_t len) { + if (is_valid_identifier(key, (ant_offset_t)klen)) { + return cpy(buf, len, key, klen); + } + ant_value_t key_str = js_mkstr(js, key, klen); + return strstring(js, key_str, buf, len); +} + static bool is_small_object(ant_t *js, ant_value_t obj, int *prop_count) { int count = 0; bool has_nested = false; - - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - ant_offset_t next = loadoff(js, obj_off) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - if (is_sym_key_prop(js, next)) { + + ant_value_t as_obj = js_as_obj(obj); + ant_object_t *ptr = js_obj_ptr(as_obj); + uintptr_t obj_off = (uintptr_t)vdata(as_obj); + if (ptr && ptr->shape) { + uint32_t shape_count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < shape_count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop) continue; + + if (prop->type == ANT_SHAPE_KEY_SYMBOL) { + count++; + continue; + } + + if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue; + + ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); + uint8_t t = vtype(val); + if (t == T_OBJ || t == T_ARR || t == T_FUNC) has_nested = true; count++; - next = next_prop(header); - continue; } - - const char *key; ant_offset_t klen; - get_prop_key(js, next, &key, &klen); - - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, klen); - if (desc && !desc->enumerable) { next = next_prop(header); continue; } - - ant_value_t val = get_prop_val(js, next); - uint8_t t = vtype(val); - if (t == T_OBJ || t == T_ARR || t == T_FUNC) has_nested = true; - count++; - - next = next_prop(header); } - descriptor_entry_t *desc, *tmp; - HASH_ITER(hh, desc_registry, desc, tmp) { - if (desc->obj_off != obj_off) continue; - if (!desc->enumerable) continue; - if (!desc->has_getter && !desc->has_setter) continue; - count++; + if (ptr && ptr->is_exotic) { + descriptor_entry_t *desc, *tmp; + HASH_ITER(hh, desc_registry, desc, tmp) { + if (desc->obj_off != obj_off) continue; + if (!desc->enumerable) continue; + if (!desc->has_getter && !desc->has_setter) continue; + count++; + } } if (prop_count) *prop_count = count; @@ -877,25 +1103,22 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { } ant_value_t tag_sym = get_toStringTag_sym(); - ant_offset_t tag_off = (vtype(tag_sym) == T_SYMBOL) ? lkp_sym_proto(js, obj, (ant_offset_t)vdata(tag_sym)) : 0; + ant_value_t tag_val = (vtype(tag_sym) == T_SYMBOL) ? lkp_sym_proto_val(js, obj, (ant_offset_t)vdata(tag_sym)) : js_mkundef(); bool is_map = false, is_set = false, is_arraybuffer = false; ant_offset_t tlen = 0, toff = 0; const char *tag_str = NULL; int prop_count = 0; bool inline_mode = false; - if (tag_off == 0) goto print_plain_object; - - ant_value_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); if (vtype(tag_val) != T_STR) goto print_plain_object; toff = vstr(js, tag_val, &tlen); - tag_str = (const char *) &js->mem[toff]; + tag_str = (const char *)(uintptr_t)(toff); is_map = (tlen == 3 && memcmp(tag_str, "Map", 3) == 0); is_set = (tlen == 3 && memcmp(tag_str, "Set", 3) == 0); is_arraybuffer = (tlen >= 11 && memcmp(tag_str + tlen - 11, "ArrayBuffer", 11) == 0); - ant_value_t ta_slot = js_get_slot(js, obj, SLOT_BUFFER); + ant_value_t ta_slot = js_get_slot(obj, SLOT_BUFFER); if (vtype(ta_slot) == T_TYPEDARRAY) { TypedArrayData *ta = (TypedArrayData *)vdata(ta_slot); if (ta && ta->buffer) { @@ -976,7 +1199,7 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { } if (is_arraybuffer) { - ant_value_t buf_val = js_get_slot(js, obj, SLOT_BUFFER); + ant_value_t buf_val = js_get_slot(obj, SLOT_BUFFER); if (vtype(buf_val) == T_NUM) { ArrayBufferData *ab_data = (ArrayBufferData *)(uintptr_t)tod(buf_val); size_t bytelen = ab_data ? ab_data->length : 0; @@ -1003,7 +1226,7 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { bool is_dataview = (tlen == 8 && memcmp(tag_str, "DataView", 8) == 0); if (is_dataview) { - ant_value_t dv_data_val = js_get_slot(js, obj, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(obj, SLOT_DATA); if (vtype(dv_data_val) == T_NUM) { DataViewData *dv = (DataViewData *)(uintptr_t)tod(dv_data_val); if (dv && dv->buffer) { @@ -1035,7 +1258,7 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { } if (is_map) { - ant_value_t map_val = js_get_slot(js, obj, SLOT_MAP); + ant_value_t map_val = js_get_slot(obj, SLOT_MAP); if (vtype(map_val) == T_UNDEF) goto print_tagged_object; map_entry_t **map_ptr = (map_entry_t**)(size_t)tod(map_val); @@ -1077,7 +1300,7 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { } if (is_set) { - ant_value_t set_val = js_get_slot(js, obj, SLOT_SET); + ant_value_t set_val = js_get_slot(obj, SLOT_SET); if (vtype(set_val) == T_UNDEF) goto print_tagged_object; set_entry_t **set_ptr = (set_entry_t**)(size_t)tod(set_val); @@ -1114,7 +1337,7 @@ static size_t strobj(ant_t *js, ant_value_t obj, char *buf, size_t len) { print_tagged_object: n += cpy(buf + n, REMAIN(n, len), "Object [", 8); - n += cpy(buf + n, REMAIN(n, len), (const char *) &js->mem[toff], tlen); + n += cpy(buf + n, REMAIN(n, len), (const char *)(uintptr_t)(toff), tlen); n += cpy(buf + n, REMAIN(n, len), "] {\n", 4); goto continue_object_print; @@ -1171,47 +1394,52 @@ print_plain_object: continue_object_print:; if (!inline_mode) stringify_indent++; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(js_as_obj(obj))) & ~(3U | FLAGMASK); bool first = true; - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - int prop_capacity = 64; - - ant_offset_t *prop_offsets = malloc(prop_capacity * sizeof(ant_offset_t)); - int num_props = 0; - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - if (is_sym_key_prop(js, next)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, klen); - if (desc && !desc->enumerable) { next = next_prop(header); continue; } - - if (num_props >= prop_capacity) { - prop_capacity *= 2; - prop_offsets = realloc(prop_offsets, prop_capacity * sizeof(ant_offset_t)); + ant_value_t as_obj = js_as_obj(obj); + ant_object_t *ptr = js_obj_ptr(as_obj); + uintptr_t obj_off = (uintptr_t)vdata(as_obj); + uint32_t shape_count = (ptr && ptr->shape) ? ant_shape_count(ptr->shape) : 0; + + for (uint32_t i = 0; i < shape_count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop) continue; + if ((ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) == 0) continue; + + ant_value_t val = (i < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, i) : js_mkundef(); + + if (prop->type == ANT_SHAPE_KEY_SYMBOL) { + ant_offset_t sym_off = prop->key.sym_off; + if (vtype(tag_sym) == T_SYMBOL && sym_off == (ant_offset_t)vdata(tag_sym)) continue; + + if (ptr && ptr->is_exotic) { + prop_meta_t meta; + if (lookup_symbol_prop_meta(as_obj, sym_off, &meta) && !meta.enumerable) continue; + } + + ant_value_t sym = mkval(T_SYMBOL, sym_off); + + if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); + first = false; + if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); + n += cpy(buf + n, REMAIN(n, len), "[", 1); + n += tostr(js, sym, buf + n, REMAIN(n, len)); + n += cpy(buf + n, REMAIN(n, len), "]: ", 3); + n += tostr(js, val, buf + n, REMAIN(n, len)); + continue; } - prop_offsets[num_props++] = next; - next = next_prop(header); - } - - for (int i = 0; i < num_props; i++) { - ant_offset_t prop = prop_offsets[i]; - ant_offset_t koff = loadoff(js, prop + (ant_offset_t) sizeof(prop)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - ant_value_t val = loadval(js, prop + (ant_offset_t) (sizeof(prop) + sizeof(koff))); - + + const char *key = prop->key.interned; + ant_offset_t klen = (ant_offset_t)strlen(key); + if (ptr && ptr->is_exotic) { + prop_meta_t meta; + if (lookup_string_prop_meta(js, as_obj, key, (size_t)klen, &meta) && !meta.enumerable) continue; + } + if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); first = false; if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); - + bool is_special_global = false; if (vtype(val) == T_UNDEF && streq(key, klen, "undefined", 9)) { is_special_global = true; @@ -1219,62 +1447,39 @@ continue_object_print:; double d = tod(val); if (isinf(d) && d > 0 && streq(key, klen, "Infinity", 8)) { is_special_global = true; - } else if (isnan(d) && streq(key, klen, "NaN", 3)) is_special_global = true; + } else if (isnan(d) && streq(key, klen, "NaN", 3)) { + is_special_global = true; + } } - + if (is_special_global) { n += tostr(js, val, buf + n, REMAIN(n, len)); } else { - n += strkey(js, mkval(T_STR, koff), buf + n, REMAIN(n, len)); + n += strkey_interned(js, key, (size_t)klen, buf + n, REMAIN(n, len)); n += cpy(buf + n, REMAIN(n, len), ": ", 2); n += tostr(js, val, buf + n, REMAIN(n, len)); } } - free(prop_offsets); - next = loadoff(js, obj_off) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header) || !is_sym_key_prop(js, next)) { - next = next_prop(header); continue; - } - - ant_offset_t sym_off = get_prop_koff(js, next); - if (vtype(tag_sym) == T_SYMBOL && sym_off == (ant_offset_t)vdata(tag_sym)) { - next = next_prop(header); continue; + if (ptr && ptr->is_exotic) { + descriptor_entry_t *desc, *tmp; + HASH_ITER(hh, desc_registry, desc, tmp) { + if (desc->obj_off != obj_off) continue; + if (!desc->enumerable) continue; + if (!desc->has_getter && !desc->has_setter) continue; + + if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); + first = false; + if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); + n += cpy(buf + n, REMAIN(n, len), desc->prop_name, desc->prop_len); + n += cpy(buf + n, REMAIN(n, len), ": ", 2); + + if (desc->has_getter && desc->has_setter) { + n += cpy(buf + n, REMAIN(n, len), "[Getter/Setter]", 15); + } else if (desc->has_getter) { + n += cpy(buf + n, REMAIN(n, len), "[Getter]", 8); + } else n += cpy(buf + n, REMAIN(n, len), "[Setter]", 8); } - - ant_value_t sym = mkval(T_SYMBOL, sym_off); - ant_value_t val = loadval(js, next + (ant_offset_t)(sizeof(ant_offset_t) * 2)); - - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); - first = false; - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); - n += cpy(buf + n, REMAIN(n, len), "[", 1); - n += tostr(js, sym, buf + n, REMAIN(n, len)); - n += cpy(buf + n, REMAIN(n, len), "]: ", 3); - n += tostr(js, val, buf + n, REMAIN(n, len)); - - next = next_prop(header); - } - - descriptor_entry_t *desc, *tmp; - HASH_ITER(hh, desc_registry, desc, tmp) { - if (desc->obj_off != obj_off) continue; - if (!desc->enumerable) continue; - if (!desc->has_getter && !desc->has_setter) continue; - - if (!first) n += cpy(buf + n, REMAIN(n, len), inline_mode ? ", " : ",\n", 2); - first = false; - if (!inline_mode) n += add_indent(buf + n, REMAIN(n, len), stringify_indent); - n += cpy(buf + n, REMAIN(n, len), desc->prop_name, desc->prop_len); - n += cpy(buf + n, REMAIN(n, len), ": ", 2); - - if (desc->has_getter && desc->has_setter) { - n += cpy(buf + n, REMAIN(n, len), "[Getter/Setter]", 15); - } else if (desc->has_getter) { - n += cpy(buf + n, REMAIN(n, len), "[Getter]", 8); - } else n += cpy(buf + n, REMAIN(n, len), "[Setter]", 8); } if (!inline_mode) stringify_indent--; @@ -1344,89 +1549,68 @@ static size_t strnum(ant_value_t value, char *buf, size_t len) { return cpy(buf, len, temp, strlen(temp)); } -static inline ant_offset_t assert_string_header(ant_t *js, ant_value_t value, ant_offset_t *out_off) { - assert(vtype(value) == T_STR); - ant_offset_t off = (ant_offset_t) vdata(value); - assert(off + sizeof(ant_offset_t) <= js->brk); - ant_offset_t header = loadoff(js, off); - assert((header & 3) == T_STR); - if (out_off) *out_off = off; - return header; -} - -static inline ant_offset_t assert_flat_string_len(ant_t *js, ant_value_t value, ant_offset_t *out_off) { - ant_offset_t off; - ant_offset_t header = assert_string_header(js, value, &off); - assert((header & ROPE_FLAG) == 0); - ant_offset_t len = offtolen(header); - assert(off + sizeof(ant_offset_t) + len <= js->brk); - if (out_off) *out_off = off; +static inline ant_offset_t assert_flat_string_len(ant_t *js, ant_value_t value, const char **out_ptr) { + (void)js; + ant_flat_string_t *flat = str_flat_ptr(value); + assert(flat != NULL); + ant_offset_t len = flat->len; + if (out_ptr) *out_ptr = flat->bytes; return len; } -static inline ant_offset_t assert_rope_header(ant_t *js, ant_value_t value, ant_offset_t *out_off) { - ant_offset_t off; - ant_offset_t header = assert_string_header(js, value, &off); - assert((header & ROPE_FLAG) != 0); - assert(off + sizeof(rope_node_t) <= js->brk); - ant_offset_t payload_header = header & ~(ROPE_FLAG | (ROPE_DEPTH_MASK << ROPE_DEPTH_SHIFT)); - assert((payload_header >> 3) > 0); - if (out_off) *out_off = off; - return header; -} - -bool is_rope(ant_t *js, ant_value_t value) { - ant_offset_t header = assert_string_header(js, value, NULL); - if ((header & ROPE_FLAG) != 0) assert_rope_header(js, value, NULL); - return (header & ROPE_FLAG) != 0; +static inline ant_rope_heap_t *assert_rope_ptr(ant_value_t value) { + assert(vtype(value) == T_STR); + assert(str_is_heap_rope(value)); + ant_rope_heap_t *ptr = str_rope_ptr(value); + assert(ptr != NULL); + return ptr; } -static inline ant_offset_t rope_len(ant_t *js, ant_value_t value) { - ant_offset_t header = assert_rope_header(js, value, NULL); - return offtolen(header & ~(ROPE_FLAG | (ROPE_DEPTH_MASK << ROPE_DEPTH_SHIFT))); +static inline ant_offset_t rope_len(ant_value_t value) { + ant_rope_heap_t *ptr = assert_rope_ptr(value); + return ptr->len; } -static inline uint8_t rope_depth(ant_t *js, ant_value_t value) { - ant_offset_t off = (ant_offset_t) vdata(value); - ant_offset_t header = loadoff(js, off); - return (uint8_t)((header >> ROPE_DEPTH_SHIFT) & ROPE_DEPTH_MASK); +static inline uint8_t rope_depth(ant_value_t value) { + ant_rope_heap_t *ptr = assert_rope_ptr(value); + return ptr->depth; } -static inline ant_value_t rope_left(ant_t *js, ant_value_t value) { - ant_offset_t off = (ant_offset_t) vdata(value); - return loadval(js, off + offsetof(rope_node_t, left)); +static inline ant_value_t rope_left(ant_value_t value) { + ant_rope_heap_t *ptr = assert_rope_ptr(value); + return ptr->left; } -static inline ant_value_t rope_right(ant_t *js, ant_value_t value) { - ant_offset_t off = (ant_offset_t) vdata(value); - return loadval(js, off + offsetof(rope_node_t, right)); +static inline ant_value_t rope_right(ant_value_t value) { + ant_rope_heap_t *ptr = assert_rope_ptr(value); + return ptr->right; } -static inline ant_value_t rope_cached_flat(ant_t *js, ant_value_t value) { - ant_offset_t off = (ant_offset_t) vdata(value); - return loadval(js, off + offsetof(rope_node_t, cached)); +static inline ant_value_t rope_cached_flat(ant_value_t value) { + ant_rope_heap_t *ptr = assert_rope_ptr(value); + return ptr->cached; } -static inline void rope_set_cached_flat(ant_t *js, ant_value_t rope, ant_value_t flat) { - ant_offset_t off = (ant_offset_t) vdata(rope); - saveval(js, off + offsetof(rope_node_t, cached), flat); +static inline void rope_set_cached_flat(ant_value_t rope, ant_value_t flat) { + ant_rope_heap_t *ptr = assert_rope_ptr(rope); + ptr->cached = flat; } static void rope_flatten_into(ant_t *js, ant_value_t str, char *dest, ant_offset_t *pos) { assert(vtype(str) == T_STR); - if (!is_rope(js, str)) { - ant_offset_t soff; - ant_offset_t slen = assert_flat_string_len(js, str, &soff); - memcpy(dest + *pos, &js->mem[soff + sizeof(ant_offset_t)], slen); + if (!str_is_heap_rope(str)) { + const char *sptr; + ant_offset_t slen = assert_flat_string_len(js, str, &sptr); + memcpy(dest + *pos, sptr, slen); *pos += slen; return; } - ant_value_t cached = rope_cached_flat(js, str); - if (vtype(cached) == T_STR && !is_rope(js, cached)) { - ant_offset_t coff; - ant_offset_t clen = assert_flat_string_len(js, cached, &coff); - memcpy(dest + *pos, &js->mem[coff + sizeof(ant_offset_t)], clen); + ant_value_t cached = rope_cached_flat(str); + if (vtype(cached) == T_STR && !str_is_heap_rope(cached)) { + const char *cptr; + ant_offset_t clen = assert_flat_string_len(js, cached, &cptr); + memcpy(dest + *pos, cptr, clen); *pos += clen; return; } @@ -1437,36 +1621,36 @@ static void rope_flatten_into(ant_t *js, ant_value_t str, char *dest, ant_offset ant_value_t node = stack[--sp]; assert(vtype(node) == T_STR); - if (!is_rope(js, node)) { - ant_offset_t soff; - ant_offset_t slen = assert_flat_string_len(js, node, &soff); - memcpy(dest + *pos, &js->mem[soff + sizeof(ant_offset_t)], slen); + if (!str_is_heap_rope(node)) { + const char *sptr; + ant_offset_t slen = assert_flat_string_len(js, node, &sptr); + memcpy(dest + *pos, sptr, slen); *pos += slen; continue; } - ant_value_t c = rope_cached_flat(js, node); - if (vtype(c) == T_STR && !is_rope(js, c)) { - ant_offset_t coff; - ant_offset_t clen = assert_flat_string_len(js, c, &coff); - memcpy(dest + *pos, &js->mem[coff + sizeof(ant_offset_t)], clen); + ant_value_t c = rope_cached_flat(node); + if (vtype(c) == T_STR && !str_is_heap_rope(c)) { + const char *cptr; + ant_offset_t clen = assert_flat_string_len(js, c, &cptr); + memcpy(dest + *pos, cptr, clen); *pos += clen; continue; } if (sp + 2 <= ROPE_MAX_DEPTH + 8) { - stack[sp++] = rope_right(js, node); - stack[sp++] = rope_left(js, node); + stack[sp++] = rope_right(node); + stack[sp++] = rope_left(node); } } } ant_value_t rope_flatten(ant_t *js, ant_value_t rope) { assert(vtype(rope) == T_STR); - if (!is_rope(js, rope)) return rope; + if (!str_is_heap_rope(rope)) return rope; - ant_value_t cached = rope_cached_flat(js, rope); - if (vtype(cached) == T_STR && !is_rope(js, cached)) return cached; + ant_value_t cached = rope_cached_flat(rope); + if (vtype(cached) == T_STR && !str_is_heap_rope(cached)) return cached; - ant_offset_t total_len = rope_len(js, rope); + ant_offset_t total_len = rope_len(rope); char *buf = (char *)ant_calloc(total_len + 1); if (!buf) return js_mkerr(js, "oom"); @@ -1479,30 +1663,28 @@ ant_value_t rope_flatten(ant_t *js, ant_value_t rope) { free(buf); if (!is_err(flat)) { - rope_set_cached_flat(js, rope, flat); + rope_set_cached_flat(rope, flat); } return flat; } ant_offset_t vstr(ant_t *js, ant_value_t value, ant_offset_t *len) { - ant_offset_t header = assert_string_header(js, value, NULL); - - if (header & ROPE_FLAG) { + if (str_is_heap_rope(value)) { ant_value_t flat = rope_flatten(js, value); assert(!is_err(flat)); value = flat; } - ant_offset_t off; - ant_offset_t slen = assert_flat_string_len(js, value, &off); + const char *ptr = NULL; + ant_offset_t slen = assert_flat_string_len(js, value, &ptr); if (len) *len = slen; - return (ant_offset_t) (off + sizeof(off)); + return (ant_offset_t)(uintptr_t)ptr; } static size_t strstring(ant_t *js, ant_value_t value, char *buf, size_t len) { ant_offset_t slen, off = vstr(js, value, &slen); - const char *str = (const char *) &js->mem[off]; + const char *str = (const char *)(uintptr_t)off; size_t n = 0; n += cpy(buf + n, REMAIN(n, len), "'", 1); for (ant_offset_t i = 0; i < slen && n < len - 1; i++) { @@ -1519,9 +1701,16 @@ static size_t strstring(ant_t *js, ant_value_t value, char *buf, size_t len) { return n; } -static const char *intern_string(const char *str, size_t len) { +const char *intern_string(const char *str, size_t len) { + if (!intern_table_init()) return NULL; + + if ((intern_count + 1) * INTERN_LOAD_DEN >= intern_bucket_count * INTERN_LOAD_NUM) { + size_t next_bucket_count = intern_bucket_count << 1; + if (next_bucket_count > intern_bucket_count) intern_table_rehash(next_bucket_count); + } + uint64_t h = hash_key(str, len); - uint32_t bucket = (uint32_t)(h & (ANT_LIMIT_SIZE_CACHE - 1)); + size_t bucket = (size_t)(h & (intern_bucket_count - 1)); for (interned_string_t *e = intern_buckets[bucket]; e; e = e->next) { if (e->hash == h && e->len == len && memcmp(e->str, str, len) == 0) return e->str; @@ -1556,7 +1745,6 @@ bool is_internal_prop(const char *key, ant_offset_t klen) { if (klen < 2) return false; if (key[0] != '_' || key[1] != '_') return false; if (klen == STR_PROTO_LEN && memcmp(key, STR_PROTO, STR_PROTO_LEN) == 0) return false; - if (klen >= 9 && key[2] == 's' && key[3] == 'y' && key[4] == 'm' && key[5] == '_' && key[klen-1] == '_' && key[klen-2] == '_') return true; return true; } @@ -1578,9 +1766,9 @@ static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { const char *name = get_func_name(js, value, &name_len); ant_value_t func_obj = js_func_obj(value); - ant_value_t code_slot = get_slot(js, func_obj, SLOT_CODE); - ant_value_t builtin_slot = get_slot(js, func_obj, SLOT_BUILTIN); - ant_value_t async_slot = get_slot(js, func_obj, SLOT_ASYNC); + ant_value_t code_slot = get_slot(func_obj, SLOT_CODE); + ant_value_t builtin_slot = get_slot(func_obj, SLOT_BUILTIN); + ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC); bool is_async = (async_slot == js_true); bool has_code = (vtype(code_slot) == T_CFUNC); @@ -1598,7 +1786,7 @@ static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { } if (!has_code) { - ant_value_t cfunc_slot = get_slot(js, func_obj, SLOT_CFUNC); + ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); bool is_native = (vtype(cfunc_slot) == T_CFUNC); size_t n; @@ -1612,14 +1800,11 @@ static size_t strfunc(ant_t *js, ant_value_t value, char *buf, size_t len) { if (!is_native) return n; - ant_value_t proto = get_slot(js, func_obj, SLOT_PROTO); + ant_value_t proto = get_slot(func_obj, SLOT_PROTO); uint8_t pt = vtype(proto); if (pt != T_OBJ && pt != T_FUNC) return n; - ant_offset_t ctor_off = lkp(js, proto, "constructor", 11); - if (ctor_off == 0) return n; - - ant_value_t ctor = resolveprop(js, mkval(T_PROP, ctor_off)); + ant_value_t ctor = lkp_val(js, proto, "constructor", 11); uint8_t ct = vtype(ctor); if (ct != T_FUNC && ct != T_CFUNC) return n; @@ -1671,7 +1856,7 @@ static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len) { if (vtype(stack) == T_STR) { ant_offset_t slen; ant_offset_t off = vstr(js, stack, &slen); - return cpy(buf, len, (const char *)&js->mem[off], slen); + return cpy(buf, len, (const char *)(uintptr_t)(off), slen); } } return ANT_COPY(buf, len, "Error"); @@ -1683,7 +1868,6 @@ static size_t tostr(ant_t *js, ant_value_t value, char *buf, size_t len) { return ANT_COPY(buf, len, "Symbol()"); } - case T_PROP: return (size_t) snprintf(buf, len, "PROP@%lu", (unsigned long) vdata(value)); default: return (size_t) snprintf(buf, len, "VTYPE%d", vtype(value)); } } @@ -1711,7 +1895,7 @@ js_cstr_t js_to_cstr(ant_t *js, ant_value_t value, char *stack_buf, size_t stack if (vtype(stack) == T_STR) { ant_offset_t slen; ant_offset_t off = vstr(js, stack, &slen); - out.ptr = (const char *)&js->mem[off]; + out.ptr = (const char *)(uintptr_t)(off); out.len = slen; return out; } @@ -1782,7 +1966,7 @@ ant_value_t js_tostring_val(ant_t *js, ant_value_t value) { char *buf; size_t len, buflen; static const void *jump_table[] = { - [T_OBJ] = &&L_OBJ, [T_PROP] = &&L_DEFAULT, [T_STR] = &&L_STR, + [T_OBJ] = &&L_OBJ, [T_STR] = &&L_STR, [T_UNDEF] = &&L_UNDEF, [T_NULL] = &&L_NULL, [T_NUM] = &&L_NUM, [T_BOOL] = &&L_BOOL, [T_FUNC] = &&L_OBJ, [T_CFUNC] = &&L_DEFAULT, [T_ERR] = &&L_DEFAULT, [T_ARR] = &&L_OBJ, @@ -1831,7 +2015,7 @@ const char *js_str(ant_t *js, ant_value_t value) { ant_value_t stack = js_get(js, obj, "stack"); if (vtype(stack) == T_STR) { ant_offset_t slen, off = vstr(js, stack, &slen); - return (const char *)&js->mem[off]; + return (const char *)(uintptr_t)off; } } return "Error"; @@ -1863,162 +2047,92 @@ const char *js_str(ant_t *js, ant_value_t value) { free(buf); if (is_err(str)) return ""; - return (const char *)&js->mem[vdata(str) + sizeof(ant_offset_t)]; -} - -static bool js_try_grow_memory(ant_t *js, size_t needed) { - if (!js->owns_mem) return false; - if (js->max_size == 0) return false; - - size_t current = (size_t)js->size; - size_t required = current + needed; - size_t new_mem_size = ((required + ARENA_GROW_INCREMENT - 1) / ARENA_GROW_INCREMENT) * ARENA_GROW_INCREMENT; - - if (new_mem_size > (size_t)js->max_size) new_mem_size = (size_t)js->max_size; - if (new_mem_size <= current) return false; - - if (ant_arena_commit(js->mem, js->size, new_mem_size) != 0) return false; - js->size = (ant_offset_t)(new_mem_size / 8U * 8U); - - return true; -} - -static inline bool js_has_space(ant_t *js, size_t size) { - return js->brk + size <= js->size; -} - -static bool js_ensure_space(ant_t *js, size_t size) { - if (js_has_space(js, size)) return true; - if (js_try_grow_memory(js, size) && js_has_space(js, size)) return true; - - js->needs_gc = true; - - if (js_has_space(js, size)) return true; - if (js_try_grow_memory(js, size) && js_has_space(js, size)) return true; - - return false; -} - -static void js_track_allocation(ant_t *js, size_t size) { - js->brk += (ant_offset_t) size; - js->gc_alloc_since += (ant_offset_t) size; - - ant_offset_t threshold = js->brk / 2; - if (threshold < 4 * 1024 * 1024) threshold = 4 * 1024 * 1024; - if (js->gc_alloc_since > threshold) js->needs_gc = true; -} - -ant_offset_t js_alloc(ant_t *js, size_t size) { - size = align64((ant_offset_t) size); - if (!js_ensure_space(js, size)) return ~(ant_offset_t) 0; - - ant_offset_t ofs = js->brk; - js_track_allocation(js, size); - - return ofs; -} - -static ant_offset_t dense_alloc(ant_t *js, ant_offset_t capacity) { - ant_offset_t size = sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * capacity; - ant_offset_t off = js_alloc(js, size); - if (off == (ant_offset_t)~0) return 0; - - saveoff(js, off, capacity); - saveoff(js, off + sizeof(ant_offset_t), 0); - - for (ant_offset_t i = 0; i < capacity; i++) saveval( - js, off + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * i, T_EMPTY - ); - - return off; -} - -static inline ant_offset_t get_dense_buf(ant_t *js, ant_value_t arr) { - ant_offset_t off = (ant_offset_t) vdata(js_as_obj(arr)); - if (__builtin_expect(off >= js->brk, 0)) return 0; - ant_offset_t next = loadoff(js, off) & ~(3U | FLAGMASK); - if (__builtin_expect(next == 0 || next >= js->brk, 0)) return 0; - ant_offset_t header = loadoff(js, next); - if ((header & SLOTMASK) && - loadoff(js, next + sizeof(ant_offset_t)) == (ant_offset_t)SLOT_DENSE_BUF) { - ant_value_t slot = loadval(js, next + sizeof(ant_offset_t) * 2); - return (ant_offset_t) tod(slot); - } - ant_value_t slot = get_slot(js, arr, SLOT_DENSE_BUF); - if (vtype(slot) == T_UNDEF) return 0; - return (ant_offset_t) tod(slot); + ant_offset_t off = vstr(js, str, NULL); + return (const char *)(uintptr_t)off; } -static inline ant_offset_t get_dense_buf_off(ant_t *js, ant_offset_t obj_off) { - return get_dense_buf(js, mkval(T_ARR, (uint64_t)obj_off)); +static inline ant_offset_t get_dense_buf(ant_value_t arr) { + ant_object_t *ptr = js_obj_ptr(js_as_obj(arr)); + if (!ptr || !ptr->fast_array || !ptr->u.array.data || ptr->u.array.cap == 0) return 0; + return (ant_offset_t)(uintptr_t)ptr; } -static inline ant_offset_t dense_capacity(ant_t *js, ant_offset_t doff) { - return loadoff(js, doff); +static inline ant_object_t *dense_obj(ant_offset_t doff) { + if (!doff) return NULL; + ant_object_t *ptr = (ant_object_t *)(uintptr_t)doff; + if (!ptr || !ptr->fast_array || !ptr->u.array.data || ptr->u.array.cap == 0) return NULL; + return ptr; } -static inline ant_offset_t dense_length(ant_t *js, ant_offset_t doff) { - return loadoff(js, doff + sizeof(ant_offset_t)); +static inline ant_value_t *dense_data(ant_offset_t doff) { + ant_object_t *ptr = dense_obj(doff); + return ptr ? ptr->u.array.data : NULL; } -static inline void dense_set_length(ant_t *js, ant_offset_t doff, ant_offset_t len) { - saveoff(js, doff + sizeof(ant_offset_t), len); +static inline ant_offset_t dense_capacity(ant_offset_t doff) { + ant_object_t *ptr = dense_obj(doff); + return ptr ? (ant_offset_t)ptr->u.array.cap : 0; } -static inline ant_value_t dense_get(ant_t *js, ant_offset_t doff, ant_offset_t idx) { - return loadval(js, doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * idx); +static inline ant_value_t dense_get(ant_offset_t doff, ant_offset_t idx) { + ant_object_t *ptr = dense_obj(doff); + if (!ptr || idx >= ptr->u.array.cap) return js_mkundef(); + return ptr->u.array.data[idx]; } static inline void dense_set(ant_t *js, ant_offset_t doff, ant_offset_t idx, ant_value_t val) { - saveval(js, doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * idx, val); + ant_object_t *ptr = dense_obj(doff); + if (!ptr || idx >= ptr->u.array.cap) return; + ptr->u.array.data[idx] = val; + gc_write_barrier(js, ptr, val); } static ant_offset_t dense_grow(ant_t *js, ant_value_t arr, ant_offset_t needed) { - ant_offset_t doff = get_dense_buf(js, arr); - ant_offset_t old_cap = doff ? dense_capacity(js, doff) : 0; - ant_offset_t old_len = doff ? dense_length(js, doff) : 0; + ant_object_t *obj = js_obj_ptr(js_as_obj(arr)); + if (!obj) return 0; + + ant_offset_t old_cap = obj->u.array.cap; ant_offset_t new_cap = old_cap ? old_cap : MAX_DENSE_INITIAL_CAP; while (new_cap < needed) new_cap *= 2; - ant_offset_t new_doff = dense_alloc(js, new_cap); - - if (new_doff == 0) return 0; - if (doff && old_len > 0) memcpy( - &js->mem[new_doff + sizeof(ant_offset_t) * 2], - &js->mem[doff + sizeof(ant_offset_t) * 2], - sizeof(ant_value_t) * old_len - ); - - dense_set_length(js, new_doff, old_len); - set_slot(js, arr, SLOT_DENSE_BUF, tov((double)new_doff)); - - return new_doff; + ant_value_t *next = realloc(obj->u.array.data, sizeof(*next) * (size_t)new_cap); + if (!next) return 0; + + for (ant_offset_t i = old_cap; i < new_cap; i++) next[i] = T_EMPTY; + + obj->u.array.data = next; + obj->u.array.cap = (uint32_t)new_cap; + if (obj->u.array.len > obj->u.array.cap) obj->u.array.len = obj->u.array.cap; + obj->fast_array = 1; + return (ant_offset_t)(uintptr_t)obj; } +// TODO: make get and set dry static inline ant_value_t arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx) { - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t semantic_len = get_array_length(js, arr); + + if (idx >= semantic_len) return js_mkundef(); + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); + ant_offset_t len = dense_iterable_length(js, arr); if (idx < len) { - ant_value_t v = dense_get(js, doff, idx); + ant_value_t v = dense_get(doff, idx); if (!is_empty_slot(v)) return v; - return js_mkundef(); } } char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); - ant_offset_t prop = lkp(js, arr, idxstr, idxlen); - return prop ? resolveprop(js, mkval(T_PROP, prop)) : js_mkundef(); + return lkp_val(js, arr, idxstr, idxlen); } static inline void arr_set(ant_t *js, ant_value_t arr, ant_offset_t idx, ant_value_t val) { - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); + ant_offset_t len = dense_iterable_length(js, arr); if (idx < len) { dense_set(js, doff, idx, val); @@ -2028,18 +2142,18 @@ static inline void arr_set(ant_t *js, ant_value_t arr, ant_offset_t idx, ant_val ant_offset_t density_limit = len > 0 ? len * 4 : 64; if (idx >= density_limit) goto sparse; - ant_offset_t cap = dense_capacity(js, doff); + ant_offset_t cap = dense_capacity(doff); if (idx >= cap) { doff = dense_grow(js, arr, idx + 1); if (doff == 0) goto sparse; } for (ant_offset_t i = len; i < idx; i++) { - ant_value_t v = dense_get(js, doff, i); + ant_value_t v = dense_get(doff, i); if (!is_empty_slot(v) && vtype(v) == T_UNDEF) dense_set(js, doff, i, T_EMPTY); } dense_set(js, doff, idx, val); - dense_set_length(js, doff, idx + 1); + array_len_set(js, arr, idx + 1); return; } @@ -2052,86 +2166,117 @@ static inline void arr_set(ant_t *js, ant_value_t arr, ant_offset_t idx, ant_val } static inline bool arr_has(ant_t *js, ant_value_t arr, ant_offset_t idx) { - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t semantic_len = get_array_length(js, arr); + if (idx >= semantic_len) return false; + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); - if (idx < len) return !is_empty_slot(dense_get(js, doff, idx)); + ant_offset_t len = dense_iterable_length(js, arr); + if (idx < len) { + ant_value_t v = dense_get(doff, idx); + if (!is_empty_slot(v)) return true; + } } char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); + return lkp(js, arr, idxstr, idxlen) != 0; } static inline void arr_del(ant_t *js, ant_value_t arr, ant_offset_t idx) { - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t semantic_len = get_array_length(js, arr); + if (idx >= semantic_len) return; + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); + ant_offset_t len = dense_iterable_length(js, arr); if (idx < len) dense_set(js, doff, idx, T_EMPTY); - return; } char idxstr[16]; - uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); - js_del(js, arr, idxstr); + size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)idx); + js_delete_prop(js, arr, idxstr, idxlen); } -static ant_value_t mkentity(ant_t *js, ant_offset_t b, const void *buf, size_t len) { - ant_offset_t ofs = js_alloc(js, len + sizeof(b)); - if (ofs == (ant_offset_t) ~0) return js_mkerr(js, "oom"); - - memcpy(&js->mem[ofs], &b, sizeof(b)); - if (buf != NULL) { - size_t copy_len = ((b & 3) == T_STR && len > 0) ? len - 1 : len; - memmove(&js->mem[ofs + sizeof(b)], buf, copy_len); - } - - if ((b & 3) == T_STR) js->mem[ofs + sizeof(b) + len - 1] = 0; - return mkval(b & 3, ofs); +ant_value_t js_mkstr(ant_t *js, const void *ptr, size_t len) { + ant_flat_string_t *flat = (ant_flat_string_t *)js_type_alloc( + js, ANT_ALLOC_STRING, sizeof(*flat) + len + 1, _Alignof(ant_flat_string_t) + ); + if (!flat) return js_mkerr(js, "oom"); + + flat->len = (ant_offset_t)len; + if (ptr && len > 0) memcpy(flat->bytes, ptr, len); + flat->bytes[len] = '\0'; + return mkval(T_STR, (uintptr_t)flat); } -ant_value_t js_mkstr(ant_t *js, const void *ptr, size_t len) { - ant_offset_t n = (ant_offset_t) (len + 1); - return mkentity(js, (ant_offset_t) ((n << 3) | T_STR), ptr, n); +ant_value_t js_mkstr_permanent(ant_t *js, const void *ptr, size_t len) { + size_t size = sizeof(ant_flat_string_t) + len + 1; + size_t align = _Alignof(ant_flat_string_t); + + if (js->pool.permanent.block_size == 0) + js->pool.permanent.block_size = ANT_POOL_STRING_BLOCK_SIZE; + ant_flat_string_t *flat = (ant_flat_string_t *)pool_alloc_chain( + &js->pool.permanent.head, NULL, js->pool.permanent.block_size, size, align + ); + if (!flat) return js_mkerr(js, "oom"); + + flat->len = (ant_offset_t)len; + if (ptr && len > 0) memcpy(flat->bytes, ptr, len); + flat->bytes[len] = '\0'; + return mkval(T_STR, (uintptr_t)flat); } static ant_value_t js_mkrope(ant_t *js, ant_value_t left, ant_value_t right, ant_offset_t total_len, uint8_t depth) { - ant_offset_t ofs = js_alloc(js, sizeof(rope_node_t)); - if (ofs == (ant_offset_t) ~0) return js_mkerr(js, "oom"); - - ant_offset_t header = ((total_len + 1) << 3) | T_STR | ROPE_FLAG | ((ant_offset_t)depth << ROPE_DEPTH_SHIFT); - ant_value_t undef = js_mkundef(); - - memcpy(&js->mem[ofs + offsetof(rope_node_t, header)], &header, sizeof(header)); - memcpy(&js->mem[ofs + offsetof(rope_node_t, left)], &left, sizeof(left)); - memcpy(&js->mem[ofs + offsetof(rope_node_t, right)], &right, sizeof(right)); - memcpy(&js->mem[ofs + offsetof(rope_node_t, cached)], &undef, sizeof(undef)); - - return mkval(T_STR, ofs); + ant_rope_heap_t *rope = (ant_rope_heap_t *)js_type_alloc( + js, ANT_ALLOC_ROPE, sizeof(*rope), _Alignof(ant_rope_heap_t) + ); + if (!rope) return js_mkerr(js, "oom"); + rope->len = total_len; + rope->depth = depth; + rope->left = left; + rope->right = right; + rope->cached = js_mkundef(); + return mkrope_value(rope); } +static ant_value_t mkobj_with_inobj_limit(ant_t *js, ant_offset_t parent, uint8_t inobj_limit) { + (void)parent; + ant_object_t *obj = obj_alloc(js, T_OBJ, inobj_limit); + if (!obj) return js_mkerr(js, "oom"); + return mkval(T_OBJ, (uintptr_t)obj); +} + ant_value_t mkobj(ant_t *js, ant_offset_t parent) { - ant_offset_t buf[2] = { parent, 0 }; - return mkentity(js, 0 | T_OBJ, buf, sizeof(buf)); + return mkobj_with_inobj_limit(js, parent, (uint8_t)ANT_INOBJ_MAX_SLOTS); +} + +ant_value_t js_mkobj_with_inobj_limit(ant_t *js, uint8_t inobj_limit) { + return mkobj_with_inobj_limit(js, 0, inobj_limit); } ant_value_t mkarr(ant_t *js) { - ant_value_t arr = mkobj(js, 0); - ant_offset_t off = (ant_offset_t) vdata(arr); - ant_offset_t header = loadoff(js, off); - - saveoff(js, off, header | ARRMASK); + ant_object_t *obj = obj_alloc(js, T_ARR, (uint8_t)ANT_INOBJ_MAX_SLOTS); + if (!obj) return js_mkerr(js, "oom"); + ant_value_t arr = mkval(T_ARR, (uintptr_t)obj); ant_value_t array_proto = get_ctor_proto(js, "Array", 5); - if (vtype(array_proto) == T_OBJ) set_proto(js, arr, array_proto); - - ant_value_t arr_val = mkval(T_ARR, vdata(arr)); - ant_offset_t doff = dense_alloc(js, MAX_DENSE_INITIAL_CAP); - if (doff) set_slot(js, arr_val, SLOT_DENSE_BUF, tov((double)doff)); - - return arr_val; + if (vtype(array_proto) == T_OBJ) js_set_proto_init(arr, array_proto); + + obj->u.array.cap = MAX_DENSE_INITIAL_CAP; + obj->u.array.len = 0; + obj->u.array.data = malloc(sizeof(*obj->u.array.data) * (size_t)obj->u.array.cap); + if (obj->u.array.data) { + for (uint32_t i = 0; i < obj->u.array.cap; i++) obj->u.array.data[i] = T_EMPTY; + obj->fast_array = 1; + } else { + obj->u.array.cap = 0; + obj->u.array.len = 0; + obj->fast_array = 0; + } + + return arr; } ant_value_t js_mkarr(ant_t *js) { @@ -2141,12 +2286,12 @@ ant_value_t js_mkarr(ant_t *js) { ant_value_t js_newobj(ant_t *js) { ant_value_t obj = mkobj(js, 0); ant_value_t proto = get_ctor_proto(js, "Object", 6); - if (vtype(proto) == T_OBJ) set_proto(js, obj, proto); + if (vtype(proto) == T_OBJ) js_set_proto_init(obj, proto); return obj; } ant_offset_t js_arr_len(ant_t *js, ant_value_t arr) { - if (vtype(arr) != T_ARR) return 0; + if (!array_obj_ptr(arr)) return 0; return get_array_length(js, arr); } @@ -2156,13 +2301,23 @@ ant_value_t js_arr_get(ant_t *js, ant_value_t arr, ant_offset_t idx) { } static inline bool is_const_prop(ant_t *js, ant_offset_t propoff) { - ant_offset_t v = loadoff(js, propoff); - return (v & CONSTMASK) != 0; + ant_prop_ref_t *ref = propref_get(js, propoff); + if (!ref) return false; + uint8_t attrs = ant_shape_get_attrs(ref->obj->shape, ref->slot); + return (attrs & ANT_PROP_ATTR_WRITABLE) == 0; +} + +static inline const ant_shape_prop_t *prop_shape_meta(ant_t *js, ant_offset_t propoff) { + ant_prop_ref_t *ref = propref_get(js, propoff); + if (!ref || !ref->obj || !ref->obj->shape) return NULL; + return ant_shape_prop_at(ref->obj->shape, ref->slot); } static inline bool is_nonconfig_prop(ant_t *js, ant_offset_t propoff) { - ant_offset_t v = loadoff(js, propoff); - return (v & NONCONFIGMASK) != 0; + ant_prop_ref_t *ref = propref_get(js, propoff); + if (!ref) return false; + uint8_t attrs = ant_shape_get_attrs(ref->obj->shape, ref->slot); + return (attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0; } static void intern_init(void) { @@ -2190,168 +2345,167 @@ static void intern_init(void) { INTERN_IDX[9] = intern_string("9", 1); } -static void invalidate_prop_cache(ant_t *js, ant_offset_t obj_off, ant_offset_t prop_off) { - ant_offset_t koff = loadoff(js, prop_off + sizeof(ant_offset_t)); - ant_offset_t klen = (loadoff(js, koff) >> 3) - 1; - - const char *key = (char *)&js->mem[koff + sizeof(ant_offset_t)]; - const char *interned = intern_string(key, klen); - if (!interned) return; - - uint32_t cache_slot = (((uintptr_t)interned >> 3) ^ obj_off) & (ANT_LIMIT_SIZE_CACHE - 1); - intern_prop_cache_entry_t *ce = &intern_prop_cache[cache_slot]; - if (ce->obj_off == obj_off && ce->intern_ptr == interned) ce->generation = 0; -} - -ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t flags) { +ant_value_t mkprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, uint8_t attrs) { obj = js_as_obj(obj); - ant_offset_t koff_entity; - + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return js_mkerr(js, "invalid object"); + + if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT; + + uint32_t slot = 0; + bool added = false; if (vtype(k) == T_SYMBOL) { - koff_entity = (ant_offset_t)vdata(k); + ant_offset_t sym_off = (ant_offset_t)vdata(k); + int32_t found = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (found >= 0) { + slot = (uint32_t)found; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + ant_shape_set_attrs_symbol(ptr->shape, sym_off, attrs); + } else { + if (!ant_shape_add_symbol_tr(&ptr->shape, sym_off, attrs, &slot)) { + return js_mkerr(js, "oom"); + } + added = true; + } } else { - ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); - koff_entity = koff - sizeof(ant_offset_t); - const char *p = (char *) &js->mem[koff]; - (void)intern_string(p, klen); + ant_offset_t klen = 0; + ant_offset_t koff = vstr(js, k, &klen); + const char *p = (const char *)(uintptr_t)(koff); + const char *interned = intern_string(p, klen); + if (!interned) return js_mkerr(js, "oom"); + + int32_t found = ant_shape_lookup_interned(ptr->shape, interned); + if (found >= 0) { + slot = (uint32_t)found; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + ant_shape_set_attrs_interned(ptr->shape, interned, attrs); + } else { + if (!ant_shape_add_interned_tr(&ptr->shape, interned, attrs, &slot)) { + return js_mkerr(js, "oom"); + } + added = true; + } } - - ant_offset_t head = (ant_offset_t) vdata(obj); - char buf[sizeof(koff_entity) + sizeof(v)]; - - ant_offset_t header = loadoff(js, head); - ant_offset_t first_prop = header & ~(3U | FLAGMASK); - ant_offset_t tail = loadoff(js, head + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - - memcpy(buf, &koff_entity, sizeof(koff_entity)); - memcpy(buf + sizeof(koff_entity), &v, sizeof(v)); - - ant_offset_t new_prop_off = js->brk; - ant_value_t prop = mkentity(js, 0 | T_PROP | flags, buf, sizeof(buf)); - if (is_err(prop)) return prop; - - if (first_prop == 0) { - ant_offset_t new_header = new_prop_off | (header & (3U | FLAGMASK)); - saveoff(js, head, new_header); - } else { - ant_offset_t tail_header = loadoff(js, tail); - ant_offset_t new_tail_header = new_prop_off | (tail_header & (3U | FLAGMASK)); - saveoff(js, tail, new_tail_header); + + if (added && !js_obj_ensure_prop_capacity(ptr, ant_shape_count(ptr->shape))) { + return js_mkerr(js, "oom"); } - saveoff(js, head + sizeof(ant_offset_t) + sizeof(ant_offset_t), new_prop_off); - - return prop; + + if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) { + return js_mkerr(js, "oom"); + } + ant_object_prop_set_unchecked(ptr, slot, v); + gc_write_barrier(js, ptr, v); + + return v; } -static inline ant_value_t mkprop_fast(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v, ant_offset_t flags) { +ant_value_t mkprop_interned(ant_t *js, ant_value_t obj, const char *interned_key, ant_value_t v, uint8_t attrs) { obj = js_as_obj(obj); - ant_offset_t koff_entity; - if (vtype(k) == T_SYMBOL) { - koff_entity = (ant_offset_t)vdata(k); + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape || !interned_key) return js_mkerr(js, "invalid object"); + + if (!attrs) attrs = ANT_PROP_ATTR_DEFAULT; + + uint32_t slot = 0; + bool added = false; + int32_t found = ant_shape_lookup_interned(ptr->shape, interned_key); + if (found >= 0) { + slot = (uint32_t)found; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + ant_shape_set_attrs_interned(ptr->shape, interned_key, attrs); } else { - ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); - koff_entity = koff - sizeof(ant_offset_t); + if (!ant_shape_add_interned_tr(&ptr->shape, interned_key, attrs, &slot)) { + return js_mkerr(js, "oom"); + } + added = true; } - ant_offset_t head = (ant_offset_t) vdata(obj); - char buf[sizeof(koff_entity) + sizeof(v)]; - - ant_offset_t header = loadoff(js, head); - ant_offset_t first_prop = header & ~(3U | FLAGMASK); - ant_offset_t tail = loadoff(js, head + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - - memcpy(buf, &koff_entity, sizeof(koff_entity)); - memcpy(buf + sizeof(koff_entity), &v, sizeof(v)); - - ant_offset_t new_prop_off = js->brk; - ant_value_t prop = mkentity(js, 0 | T_PROP | flags, buf, sizeof(buf)); - if (is_err(prop)) return prop; - - if (first_prop == 0) { - ant_offset_t new_header = new_prop_off | (header & (3U | FLAGMASK)); - saveoff(js, head, new_header); - } else { - ant_offset_t tail_header = loadoff(js, tail); - ant_offset_t new_tail_header = new_prop_off | (tail_header & (3U | FLAGMASK)); - saveoff(js, tail, new_tail_header); + + if (added && !js_obj_ensure_prop_capacity(ptr, ant_shape_count(ptr->shape))) { + return js_mkerr(js, "oom"); } - saveoff(js, head + sizeof(ant_offset_t) + sizeof(ant_offset_t), new_prop_off); - - return prop; + if (slot >= ptr->prop_count && !js_obj_ensure_prop_capacity(ptr, slot + 1)) { + return js_mkerr(js, "oom"); + } + ant_object_prop_set_unchecked(ptr, slot, v); + gc_write_barrier(js, ptr, v); + return v; } ant_value_t js_mkprop_fast(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) { ant_value_t k = js_mkstr(js, key, len); if (is_err(k)) return k; - return mkprop_fast(js, obj, k, v, 0); + return mkprop(js, obj, k, v, 0); } ant_offset_t js_mkprop_fast_off(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) { ant_value_t k = js_mkstr(js, key, len); if (is_err(k)) return 0; - ant_offset_t prop_off = js->brk; - mkprop_fast(js, obj, k, v, 0); - return prop_off + sizeof(ant_offset_t) * 2; + ant_value_t prop = mkprop(js, obj, k, v, 0); + if (is_err(prop)) return 0; + return lkp(js, obj, key, len); } -void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v) { saveval(js, off, v); } +void js_saveval(ant_t *js, ant_offset_t off, ant_value_t v) { + bool ok = propref_store(js, off, v); + assert(ok && "js_saveval expects a valid property handle"); + (void)ok; +} -static ant_value_t mkslot(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t v) { - obj = js_as_obj(obj); - ant_offset_t head = (ant_offset_t) vdata(obj); - char buf[sizeof(ant_offset_t) + sizeof(v)]; - - ant_offset_t header = loadoff(js, head); - ant_offset_t first_prop = header & ~(3U | FLAGMASK); - - ant_offset_t slot_key = (ant_offset_t)slot; - memcpy(buf, &slot_key, sizeof(slot_key)); - memcpy(buf + sizeof(slot_key), &v, sizeof(v)); - - ant_offset_t new_prop_off = js->brk; - ant_value_t prop = mkentity(js, first_prop | T_PROP | SLOTMASK, buf, sizeof(buf)); - if (is_err(prop)) return prop; - - ant_offset_t new_header = new_prop_off | (header & (3U | FLAGMASK)); - saveoff(js, head, new_header); - - if (first_prop == 0) { - saveoff(js, head + sizeof(ant_offset_t) + sizeof(ant_offset_t), new_prop_off); +static void set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t val) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || slot < 0 || slot > SLOT_MAX) return; + if (slot == SLOT_PROTO) { + ptr->proto = val; + ant_ic_epoch_bump(); + return; } - - return prop; + if (slot == SLOT_DATA) { + ptr->u.data.value = val; + return; + } + (void)obj_extra_set(ptr, slot, val); } -static inline ant_offset_t search_slot(ant_t *js, ant_value_t obj, internal_slot_t slot) { - obj = js_as_obj(obj); - ant_offset_t off = (ant_offset_t) vdata(obj); - if (__builtin_expect(off >= js->brk, 0)) return 0; - ant_offset_t next = loadoff(js, off) & ~(3U | FLAGMASK); - while (next != 0 && next < js->brk) { - ant_offset_t header = loadoff(js, next); - if ((header & SLOTMASK) == 0) return 0; - ant_offset_t koff = loadoff(js, next + sizeof(ant_offset_t)); - if (koff == (ant_offset_t)slot) return next; - next = header & ~(3U | FLAGMASK); +static void set_slot_wb(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t val) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || slot < 0 || slot > SLOT_MAX) return; + if (slot == SLOT_PROTO) { + ptr->proto = val; + gc_write_barrier(js, ptr, val); + ant_ic_epoch_bump(); + return; } - return 0; + if (slot == SLOT_DATA) { + ptr->u.data.value = val; + gc_write_barrier(js, ptr, val); + return; + } + (void)obj_extra_set(ptr, slot, val); + gc_write_barrier(js, ptr, val); } -static void set_slot(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t val) { - ant_offset_t existing = search_slot(js, obj, slot); - if (existing > 0) { - saveval(js, existing + sizeof(ant_offset_t) * 2, val); - } else mkslot(js, obj, slot, val); +void js_set_slot(ant_value_t obj, internal_slot_t slot, ant_value_t value) { + set_slot(js_as_obj(obj), slot, value); } -static ant_value_t get_slot(ant_t *js, ant_value_t obj, internal_slot_t slot) { - ant_offset_t off = search_slot(js, obj, slot); - if (off == 0) return js_mkundef(); - return loadval(js, off + sizeof(ant_offset_t) * 2); +void js_set_slot_wb(ant_t *js, ant_value_t obj, internal_slot_t slot, ant_value_t value) { + set_slot_wb(js, js_as_obj(obj), slot, value); +} + +static ant_value_t get_slot(ant_value_t obj, internal_slot_t slot) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || slot < 0 || slot > SLOT_MAX) return js_mkundef(); + if (slot == SLOT_PROTO) return ptr->proto; + if (slot == SLOT_DATA) { + return ptr->u.data.value; + } + return obj_extra_get(ptr, slot); } static void set_func_code_ptr(ant_t *js, ant_value_t func_obj, const char *code, size_t len) { - set_slot(js, func_obj, SLOT_CODE, mkval(T_CFUNC, (size_t)code)); - set_slot(js, func_obj, SLOT_CODE_LEN, tov((double)len)); + set_slot(func_obj, SLOT_CODE, mkval(T_CFUNC, (size_t)code)); + set_slot(func_obj, SLOT_CODE_LEN, tov((double)len)); } static void set_func_code(ant_t *js, ant_value_t func_obj, const char *code, size_t len) { @@ -2365,14 +2519,14 @@ static void set_func_code(ant_t *js, ant_value_t func_obj, const char *code, siz char *vars = OXC_get_func_hoisted_vars(code, len, &vars_buf_len); if (vars) { - set_slot(js, func_obj, SLOT_HOISTED_VARS, mkval(T_CFUNC, (size_t)vars)); - set_slot(js, func_obj, SLOT_HOISTED_VARS_LEN, tov((double)vars_buf_len)); + set_slot(func_obj, SLOT_HOISTED_VARS, mkval(T_CFUNC, (size_t)vars)); + set_slot(func_obj, SLOT_HOISTED_VARS_LEN, tov((double)vars_buf_len)); } } static const char *get_func_code(ant_t *js, ant_value_t func_obj, ant_offset_t *len) { - ant_value_t code_val = get_slot(js, func_obj, SLOT_CODE); - ant_value_t len_val = get_slot(js, func_obj, SLOT_CODE_LEN); + ant_value_t code_val = get_slot(func_obj, SLOT_CODE); + ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN); if (vtype(code_val) != T_CFUNC) { if (len) *len = 0; @@ -2383,14 +2537,6 @@ static const char *get_func_code(ant_t *js, ant_value_t func_obj, ant_offset_t * return (const char *)vdata(code_val); } -static inline bool is_slot_prop(ant_offset_t header) { - return (header & SLOTMASK) != 0; -} - -static inline ant_offset_t next_prop(ant_offset_t header) { - return header & ~(3U | FLAGMASK); -} - double js_to_number(ant_t *js, ant_value_t arg) { if (vtype(arg) == T_NUM) return tod(arg); if (vtype(arg) == T_BOOL) return vdata(arg) ? 1.0 : 0.0; @@ -2399,7 +2545,7 @@ double js_to_number(ant_t *js, ant_value_t arg) { if (vtype(arg) == T_STR) { ant_offset_t len, off = vstr(js, arg, &len); - const char *s = (char *)&js->mem[off], *end; + const char *s = (char *)(uintptr_t)(off), *end; while (*s == ' ' || *s == '\t' || *s == '\n' || *s == '\r') s++; if (!*s) return 0.0; double val = strtod(s, (char **)&end); @@ -2428,7 +2574,7 @@ static ant_value_t setup_func_prototype(ant_t *js, ant_value_t func) { ant_value_t object_proto = get_ctor_proto(js, "Object", 6); if (vtype(object_proto) == T_OBJ) { - set_proto(js, proto_obj, object_proto); + js_set_proto_init(proto_obj, object_proto); } ant_value_t constructor_key = js_mkstr(js, "constructor", 11); @@ -2443,11 +2589,48 @@ static ant_value_t setup_func_prototype(ant_t *js, ant_value_t func) { res = js_setprop(js, func, prototype_key, proto_obj); if (is_err(res)) return res; - js_set_descriptor(js, func, "prototype", 9, JS_DESC_W); + js_set_descriptor(js, js_as_obj(func), "prototype", 9, JS_DESC_W); + + js_mark_constructor(func, true); return js_mkundef(); } +static inline bool same_object_identity(ant_value_t a, ant_value_t b) { + if (!is_object_type(a) || !is_object_type(b)) return false; + return vdata(js_as_obj(a)) == vdata(js_as_obj(b)); +} + +static inline ant_value_t proto_next_obj_or_null(ant_t *js, ant_value_t cur) { + if (!is_object_type(cur)) return js_mknull(); + ant_value_t next = get_proto(js, cur); + return is_object_type(next) ? next : js_mknull(); +} + +typedef struct { + int depth; + bool overflow; + ant_value_t fast; +} proto_overflow_guard_t; + +static inline void proto_overflow_guard_init(proto_overflow_guard_t *g) { + g->depth = 0; + g->overflow = false; + g->fast = js_mknull(); +} + +static inline bool proto_overflow_guard_hit_cycle(ant_t *js, proto_overflow_guard_t *g, ant_value_t cur) { + if (!g->overflow) { + if (++g->depth < MAX_PROTO_CHAIN_DEPTH) return false; + g->overflow = true; + g->fast = cur; + } + + g->fast = proto_next_obj_or_null(js, g->fast); + g->fast = proto_next_obj_or_null(js, g->fast); + return same_object_identity(cur, g->fast); +} + static inline bool proto_walk_next(ant_t *js, ant_value_t *cur, uint8_t *t, uint8_t flags) { uint8_t ct = *t; @@ -2462,7 +2645,7 @@ static inline bool proto_walk_next(ant_t *js, ant_value_t *cur, uint8_t *t, uint if (ct == T_OBJ || ct == T_ARR || ct == T_FUNC || ct == T_PROMISE) { ant_value_t as_obj = js_as_obj(*cur); - ant_value_t proto = get_slot(js, as_obj, SLOT_PROTO); + ant_value_t proto = get_slot(as_obj, SLOT_PROTO); uint8_t pt = vtype(proto); if (pt == T_OBJ || pt == T_ARR || pt == T_FUNC) { @@ -2494,16 +2677,52 @@ static inline bool proto_walk_next(ant_t *js, ant_value_t *cur, uint8_t *t, uint return false; } +typedef struct { + int depth; + bool overflow; + bool fast_active; + ant_value_t fast_cur; + uint8_t fast_t; +} proto_walk_overflow_guard_t; + +static inline void proto_walk_overflow_guard_init(proto_walk_overflow_guard_t *g) { + g->depth = 0; + g->overflow = false; + g->fast_active = false; + g->fast_cur = js_mknull(); + g->fast_t = T_NULL; +} + +static inline bool proto_walk_overflow_guard_hit_cycle( + ant_t *js, + proto_walk_overflow_guard_t *g, + ant_value_t cur, + uint8_t cur_t, + uint8_t flags +) { + if (!g->overflow) { + if (++g->depth < MAX_PROTO_CHAIN_DEPTH) return false; + g->overflow = true; + g->fast_active = true; + g->fast_cur = cur; + g->fast_t = cur_t; + } + + if (g->fast_active && !proto_walk_next(js, &g->fast_cur, &g->fast_t, flags)) + g->fast_active = false; + if (g->fast_active && !proto_walk_next(js, &g->fast_cur, &g->fast_t, flags)) + g->fast_active = false; + + return g->fast_active && same_object_identity(cur, g->fast_cur); +} + ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_proto) { ant_value_t instance_proto = js_mkundef(); if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { ant_value_t nt_obj = js_as_obj(js->new_target); - ant_offset_t nt_proto_off = lkp_interned(js, nt_obj, INTERN_PROTOTYPE, 9); - if (nt_proto_off != 0) { - ant_value_t nt_proto = resolveprop(js, mkval(T_PROP, nt_proto_off)); - if (is_object_type(nt_proto)) instance_proto = nt_proto; - } + ant_value_t nt_proto = lkp_interned_val(js, nt_obj, INTERN_PROTOTYPE); + if (is_object_type(nt_proto)) instance_proto = nt_proto; } if (!is_object_type(instance_proto) && is_object_type(fallback_proto)) { @@ -2513,10 +2732,15 @@ ant_value_t js_instance_proto_from_new_target(ant_t *js, ant_value_t fallback_pr bool proto_chain_contains(ant_t *js, ant_value_t obj, ant_value_t proto_target) { if (!is_object_type(obj) || !is_object_type(proto_target)) return false; - ant_value_t cur = obj; uint8_t t = vtype(cur); - for (int depth = 0; depth < MAX_PROTO_CHAIN_DEPTH; depth++) { + ant_value_t cur = obj; + uint8_t t = vtype(cur); + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + + while (true) { if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_OBJECT_ONLY)) break; - if (vdata(cur) == vdata(proto_target)) return true; + if (same_object_identity(cur, proto_target)) return true; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } return false; } @@ -2524,7 +2748,7 @@ bool proto_chain_contains(ant_t *js, ant_value_t obj, ant_value_t proto_target) static inline bool is_wrapper_ctor_target(ant_t *js, ant_value_t this_val, ant_value_t expected_proto) { if (vtype(js->new_target) == T_UNDEF) return false; if (vtype(this_val) != T_OBJ) return false; - if (vtype(get_slot(js, this_val, SLOT_PRIMITIVE)) != T_UNDEF) return false; + if (vtype(get_slot(this_val, SLOT_PRIMITIVE)) != T_UNDEF) return false; return proto_chain_contains(js, this_val, expected_proto); } @@ -2537,18 +2761,18 @@ bool same_ctor_identity(ant_t *js, ant_value_t a, ant_value_t b) { if (vtype(a) == vtype(b) && vdata(a) == vdata(b)) return true; if (vtype(a) == T_FUNC && vtype(b) == T_CFUNC) { - ant_value_t c = get_slot(js, a, SLOT_CFUNC); + ant_value_t c = get_slot(a, SLOT_CFUNC); return vtype(c) == T_CFUNC && vdata(c) == vdata(b); } if (vtype(a) == T_CFUNC && vtype(b) == T_FUNC) { - ant_value_t c = get_slot(js, b, SLOT_CFUNC); + ant_value_t c = get_slot(b, SLOT_CFUNC); return vtype(c) == T_CFUNC && vdata(c) == vdata(a); } if (vtype(a) == T_FUNC && vtype(b) == T_FUNC) { - ant_value_t ca = get_slot(js, a, SLOT_CFUNC); - ant_value_t cb = get_slot(js, b, SLOT_CFUNC); + ant_value_t ca = get_slot(a, SLOT_CFUNC); + ant_value_t cb = get_slot(b, SLOT_CFUNC); if (vtype(ca) == T_CFUNC && vtype(cb) == T_CFUNC && vdata(ca) == vdata(cb)) return true; } @@ -2559,7 +2783,7 @@ static ant_value_t array_constructor_from_receiver(ant_t *js, ant_value_t receiv if (!is_object_type(receiver)) return js_mkundef(); ant_value_t species_source = receiver; - if (is_proxy(js, species_source)) { + if (is_proxy(species_source)) { species_source = proxy_read_target(js, species_source); } @@ -2595,7 +2819,7 @@ static ant_value_t array_alloc_from_ctor_with_length(ant_t *js, ant_value_t ctor ant_value_t proto = js_get(js, ctor, "prototype"); if (is_err(proto)) return proto; - if (is_object_type(proto)) set_proto(js, seed, proto); + if (is_object_type(proto)) js_set_proto_init(seed, proto); ant_value_t ctor_args[1] = { tov((double)length_hint) }; ant_value_t saved_new_target = js->new_target; @@ -2605,7 +2829,7 @@ static ant_value_t array_alloc_from_ctor_with_length(ant_t *js, ant_value_t ctor if (is_err(constructed)) return constructed; ant_value_t result = is_object_type(constructed) ? constructed : seed; - set_slot(js, js_as_obj(result), SLOT_CTOR, ctor); + set_slot(js_as_obj(result), SLOT_CTOR, ctor); return result; } @@ -2632,45 +2856,43 @@ static ant_value_t validate_array_length(ant_t *js, ant_value_t v) { static inline ant_value_t check_object_extensibility(ant_t *js, ant_value_t obj) { obj = js_as_obj(obj); - ant_offset_t obj_off_fp = (ant_offset_t)vdata(obj); - ant_offset_t next = loadoff(js, obj_off_fp) & ~(3U | FLAGMASK); - - while (next != 0 && next < js->brk) { - ant_offset_t hdr = loadoff(js, next); - if ((hdr & SLOTMASK) == 0) break; - - ant_offset_t sk = loadoff(js, next + sizeof(ant_offset_t)); - ant_value_t sv = loadval(js, next + sizeof(ant_offset_t) * 2); - - if (sk == (ant_offset_t)SLOT_FROZEN && js_truthy(js, sv)) { - return sv_vm_is_strict(js->vm) - ? js_mkerr(js, "cannot add property to frozen object") - : js_mkundef(); - } - - if (sk == (ant_offset_t)SLOT_SEALED && js_truthy(js, sv)) { - return sv_vm_is_strict(js->vm) - ? js_mkerr(js, "cannot add property to sealed object") - : js_mkundef(); - } - - if (sk == (ant_offset_t)SLOT_EXTENSIBLE && !js_truthy(js, sv)) { - return sv_vm_is_strict(js->vm) - ? js_mkerr(js, "cannot add property to non-extensible object") - : js_mkundef(); - } - - next = hdr & ~(3U | FLAGMASK); + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return js_mkundef(); + + if (ptr->frozen) { + return sv_vm_is_strict(js->vm) + ? js_mkerr(js, "cannot add property to frozen object") + : js_false; } - + + if (ptr->sealed) { + return sv_vm_is_strict(js->vm) + ? js_mkerr(js, "cannot add property to sealed object") + : js_false; + } + + if (!ptr->extensible) { + return sv_vm_is_strict(js->vm) + ? js_mkerr(js, "cannot add property to non-extensible object") + : js_false; + } + return js_mkundef(); } -static inline void update_array_length(ant_t *js, ant_value_t obj, ant_offset_t new_len) { - ant_offset_t len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); +static inline void array_len_set(ant_t *js, ant_value_t obj, ant_offset_t new_len) { + ant_object_t *arr_ptr = array_obj_ptr(obj); + if (arr_ptr) { + if (new_len > (ant_offset_t)UINT32_MAX) new_len = (ant_offset_t)UINT32_MAX; + arr_ptr->u.array.len = (uint32_t)new_len; + return; + } + ant_value_t new_len_val = tov((double)new_len); - - if (len_off != 0) saveval(js, len_off + sizeof(ant_offset_t) * 2, new_len_val); else { + ant_offset_t len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); + if (len_off != 0) { + js_saveval(js, len_off, new_len_val); + } else { js_mkprop_fast(js, obj, "length", 6, new_len_val); } } @@ -2679,32 +2901,139 @@ static ant_value_t js_setprop_array_fast(ant_t *js, ant_value_t obj, ant_value_t unsigned long idx; if (!parse_array_index(key, klen, (ant_offset_t)-1, &idx)) return js_mkundef(); - ant_offset_t doff = get_dense_buf(js, obj); + ant_offset_t cur_len = get_array_length(js, obj); + ant_offset_t doff = get_dense_buf(obj); if (doff) { - ant_offset_t cur_len = dense_length(js, doff); - if (idx < cur_len) { dense_set(js, doff, (ant_offset_t)idx, v); return v; } + ant_offset_t dense_len = dense_iterable_length(js, obj); + if (idx < dense_len) { dense_set(js, doff, (ant_offset_t)idx, v); return v; } - ant_offset_t density_limit = cur_len > 0 ? cur_len * 4 : 64; + ant_offset_t density_limit = dense_len > 0 ? dense_len * 4 : 64; if (idx >= density_limit) goto sparse; ant_value_t extensibility_error = check_object_extensibility(js, obj); - if (!is_undefined(extensibility_error)) return extensibility_error; + if (is_err(extensibility_error)) return extensibility_error; + if (extensibility_error == js_false) return v; arr_set(js, obj, (ant_offset_t)idx, v); return v; } sparse:; - ant_offset_t cur_len = get_array_length(js, obj); if (idx < cur_len) return js_mkundef(); ant_value_t extensibility_error = check_object_extensibility(js, obj); - if (!is_undefined(extensibility_error)) return extensibility_error; + if (is_err(extensibility_error)) return extensibility_error; + if (extensibility_error == js_false) return v; - ant_value_t result = mkprop_fast(js, obj, k, v, 0); - update_array_length(js, obj, idx + 1); + ant_value_t result = mkprop(js, obj, k, v, 0); + if (is_err(result)) return result; + array_define_or_set_index(js, obj, key, (size_t)klen); - return result; + return v; +} + +typedef enum { + PROP_META_STRING = 0, + PROP_META_SYMBOL = 1, +} prop_meta_key_t; + +static inline void prop_meta_defaults(prop_meta_t *out) { + *out = (prop_meta_t){ + .has_getter = false, + .has_setter = false, + .writable = true, + .enumerable = true, + .configurable = true, + .getter = js_mkundef(), + .setter = js_mkundef(), + }; +} + +static inline void prop_meta_from_shape(prop_meta_t *out, const ant_shape_prop_t *prop) { + out->has_getter = prop->has_getter != 0; + out->has_setter = prop->has_setter != 0; + out->writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0; + out->enumerable = (prop->attrs & ANT_PROP_ATTR_ENUMERABLE) != 0; + out->configurable = (prop->attrs & ANT_PROP_ATTR_CONFIGURABLE) != 0; + out->getter = prop->getter; + out->setter = prop->setter; +} + +static inline void prop_meta_from_desc(prop_meta_t *out, const descriptor_entry_t *desc) { + out->has_getter = desc->has_getter; + out->has_setter = desc->has_setter; + out->writable = desc->writable; + out->enumerable = desc->enumerable; + out->configurable = desc->configurable; + out->getter = desc->getter; + out->setter = desc->setter; +} + +static bool lookup_prop_meta( + ant_t *js, + ant_value_t cur_obj, + prop_meta_key_t key_kind, + const char *key, + size_t klen, + ant_offset_t sym_off, + prop_meta_t *out +) { + if (!out || !is_object_type(cur_obj)) return false; + if (key_kind == PROP_META_STRING && !key) return false; + + prop_meta_defaults(out); + + ant_object_t *cur_ptr = js_obj_ptr(cur_obj); + if (!cur_ptr) return false; + if (key_kind == PROP_META_STRING && key && is_length_key(key, klen) && + cur_ptr->type_tag == T_ARR) { + out->has_getter = false; + out->has_setter = false; + out->writable = true; + out->enumerable = false; + out->configurable = false; + out->getter = js_mkundef(); + out->setter = js_mkundef(); + return true; + } + + if (cur_ptr->shape) { + int32_t slot = -1; + if (key_kind == PROP_META_SYMBOL) { + slot = ant_shape_lookup_symbol(cur_ptr->shape, sym_off); + } else { + const char *interned_key = intern_string(key, klen); + if (interned_key) slot = ant_shape_lookup_interned(cur_ptr->shape, interned_key); + } + + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot); + if (!prop) return false; + prop_meta_from_shape(out, prop); + return true; + } + } + + if (!cur_ptr->is_exotic) return false; + + descriptor_entry_t *desc = NULL; + if (key_kind == PROP_META_SYMBOL) { + desc = lookup_sym_descriptor(cur_obj, sym_off); + } else if (js) { + desc = lookup_descriptor(cur_obj, key, klen); + } + + if (!desc) return false; + prop_meta_from_desc(out, desc); + return true; +} + +static inline bool lookup_symbol_prop_meta(ant_value_t cur_obj, ant_offset_t sym_off, prop_meta_t *out) { + return lookup_prop_meta(NULL, cur_obj, PROP_META_SYMBOL, NULL, 0, sym_off, out); +} + +static inline bool lookup_string_prop_meta(ant_t *js, ant_value_t cur_obj, const char *key, size_t klen, prop_meta_t *out) { + return lookup_prop_meta(js, cur_obj, PROP_META_STRING, key, klen, 0, out); } ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) { @@ -2712,7 +3041,7 @@ ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) if (ot == T_STR || ot == T_NUM || ot == T_BOOL) { ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); - const char *key = (char *)&js->mem[koff]; + const char *key = (char *)(uintptr_t)(koff); ant_value_t proto = get_prototype_for_type(js, ot); if (is_object_type(proto)) { ant_value_t setter = js_mkundef(); @@ -2734,73 +3063,82 @@ ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) if (vtype(k) == T_SYMBOL) { ant_offset_t sym_off = (ant_offset_t)vdata(k); ant_value_t cur = obj; - - for (int i = 0; i < MAX_PROTO_CHAIN_DEPTH; i++) { - ant_offset_t cur_off = (ant_offset_t)vdata(js_as_obj(cur)); - descriptor_entry_t *sd = lookup_sym_descriptor(cur_off, sym_off); - if (sd && sd->has_setter) { - ant_value_t setter = sd->setter; - if (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC) { - ant_value_t result = sv_vm_call(js->vm, js, setter, obj, &v, 1, NULL, false); - if (is_err(result)) return result; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + + while (is_object_type(cur)) { + ant_value_t cur_obj = js_as_obj(cur); + prop_meta_t meta; + if (lookup_symbol_prop_meta(cur_obj, sym_off, &meta)) { + if (meta.has_setter) { + ant_value_t setter = meta.setter; + if (vtype(setter) == T_FUNC || vtype(setter) == T_CFUNC) { + ant_value_t result = sv_vm_call(js->vm, js, setter, obj, &v, 1, NULL, false); + if (is_err(result)) return result; + return v; + } + } + if (meta.has_getter && !meta.has_setter) { + if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter"); return v; } + if (!meta.has_getter && !meta.has_setter && !meta.writable) { + if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "assignment to read-only property"); + return v; + } + break; } - if (sd && sd->has_getter && !sd->has_setter) { - if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter"); - return v; - } - - ant_value_t proto = get_proto(js, js_as_obj(cur)); + ant_value_t proto = get_proto(js, cur_obj); if (!is_object_type(proto)) break; cur = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } ant_offset_t existing = lkp_sym(js, obj, sym_off); if (existing > 0) { if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant"); - saveval(js, existing + sizeof(ant_offset_t) * 2, v); - return mkval(T_PROP, existing); + js_saveval(js, existing, v); + return v; } - if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to frozen object"); - return js_mkundef(); + { + ant_value_t extensibility_error = check_object_extensibility(js, obj); + if (is_err(extensibility_error)) return extensibility_error; + if (extensibility_error == js_false) return v; } return mkprop(js, obj, k, v, 0); } ant_offset_t klen; ant_offset_t koff = vstr(js, k, &klen); - const char *key = (char *) &js->mem[koff]; + const char *key = (char *)(uintptr_t)(koff); - if (vtype(obj) == T_ARR && !is_proxy(js, obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { + if (array_obj_ptr(obj) && !is_proxy(obj) && klen > 0 && key[0] >= '0' && key[0] <= '9') { ant_value_t result = js_setprop_array_fast(js, obj, k, v, klen, key); if (vtype(result) != T_UNDEF) return result; } - if (vtype(obj) == T_ARR && streq(key, klen, "length", 6)) { + if (array_obj_ptr(obj) && is_length_key(key, klen)) { ant_value_t err = validate_array_length(js, v); if (is_err(err)) return err; - ant_offset_t doff = get_dense_buf(js, obj); + ant_offset_t doff = get_dense_buf(obj); + ant_offset_t new_len_val = (ant_offset_t) tod(v); if (doff) { - ant_offset_t new_len_val = (ant_offset_t) tod(v); - ant_offset_t cur_len = dense_length(js, doff); - if (new_len_val < cur_len) { - for (ant_offset_t i = new_len_val; i < cur_len; i++) + ant_offset_t cap = dense_capacity(doff); + ant_offset_t cur_len = get_array_length(js, obj); + ant_offset_t clear_to = (cur_len < cap) ? cur_len : cap; + if (new_len_val < clear_to) { + for (ant_offset_t i = new_len_val; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY); - dense_set_length(js, doff, new_len_val); - return v; - } else if (new_len_val <= dense_capacity(js, doff)) { - dense_set_length(js, doff, new_len_val); - return v; } } + array_len_set(js, obj, new_len_val); + return v; } - if (is_proxy(js, obj)) { + if (is_proxy(obj)) { ant_value_t result = proxy_set(js, obj, key, klen, v); if (is_err(result)) return result; return v; @@ -2810,23 +3148,67 @@ ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) ant_offset_t existing = lkp(js, obj, key, klen); { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, klen); - - if (!desc) { - ant_value_t cur = obj; - for (int i = 0; i < MAX_PROTO_CHAIN_DEPTH && !desc; i++) { - cur = get_proto(js, cur); - if (vtype(cur) != T_OBJ && vtype(cur) != T_FUNC) break; - desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(cur)), key, klen); + const char *interned_key = intern_string(key, (size_t)klen); + bool found_desc = false; + bool desc_on_receiver = false; + bool desc_has_getter = false; + bool desc_has_setter = false; + bool desc_writable = true; + ant_value_t desc_setter = js_mkundef(); + + ant_value_t cur = obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + bool on_receiver = true; + while (is_object_type(cur)) { + ant_value_t cur_obj = js_as_obj(cur); + ant_object_t *cur_ptr = js_obj_ptr(cur_obj); + if (!cur_ptr) break; + + bool found_here = false; + if (cur_ptr->shape && interned_key) { + int32_t slot = ant_shape_lookup_interned(cur_ptr->shape, interned_key); + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot); + if (prop) { + found_here = true; + desc_has_getter = prop->has_getter != 0; + desc_has_setter = prop->has_setter != 0; + desc_writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0; + desc_setter = prop->setter; + } + } + } + + if (!found_here && cur_ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(cur_obj, key, klen); + if (desc) { + found_here = true; + desc_has_getter = desc->has_getter; + desc_has_setter = desc->has_setter; + desc_writable = desc->writable; + desc_setter = desc->setter; + } } - if (desc && !desc->has_setter && !desc->has_getter && desc->writable) desc = NULL; + + if (found_here) { + found_desc = true; + desc_on_receiver = on_receiver; + break; + } + + ant_value_t proto = get_proto(js, cur_obj); + if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; + cur = proto; + on_receiver = false; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } - if (!desc) goto no_descriptor; + if (!found_desc) goto no_descriptor; + if (!desc_on_receiver && !desc_has_setter && !desc_has_getter && desc_writable) goto no_descriptor; - if (desc->has_setter) { - ant_value_t setter = desc->setter; + if (desc_has_setter) { + ant_value_t setter = desc_setter; uint8_t setter_type = vtype(setter); if (setter_type == T_FUNC || setter_type == T_CFUNC) { js_error_site_t saved_errsite = js->errsite; @@ -2837,14 +3219,14 @@ ant_value_t js_setprop(ant_t *js, ant_value_t obj, ant_value_t k, ant_value_t v) } } - if (desc->has_getter && !desc->has_setter) { + if (desc_has_getter && !desc_has_setter) { if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot set property which has only a getter"); return v; } - if (!desc->writable) { + if (!desc_writable) { if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "assignment to read-only property"); - return existing > 0 ? mkval(T_PROP, existing) : v; + return v; } if (existing <= 0) goto no_descriptor; @@ -2854,74 +3236,22 @@ no_descriptor: if (existing <= 0) goto create_new; if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant"); - saveval(js, existing + sizeof(ant_offset_t) * 2, v); - if (vtype(obj) != T_ARR || klen == 0 || key[0] < '0' || key[0] > '9') goto done_update; - { ant_offset_t doff = get_dense_buf(js, obj); if (doff) goto done_update; } - - char *endptr; - unsigned long update_idx = strtoul(key, &endptr, 10); - if (endptr != key + klen) goto done_update; - - ant_offset_t len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); - ant_offset_t cur_len = 0; - if (len_off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, len_off)); - if (vtype(len_val) == T_NUM) cur_len = (ant_offset_t) tod(len_val); - } - if (update_idx < cur_len) goto done_update; - - ant_value_t new_len = tov((double)(update_idx + 1)); - if (len_off != 0) saveval(js, len_off + sizeof(ant_offset_t) * 2, new_len); else { - mkprop(js, obj, js->length_str, new_len, 0); - } - -done_update: - return mkval(T_PROP, existing); + js_saveval(js, existing, v); + array_define_or_set_index(js, obj, key, (size_t)klen); + return v; create_new: - if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to frozen object"); - return js_mkundef(); - } - - if (js_truthy(js, get_slot(js, obj, SLOT_SEALED))) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to sealed object"); - return js_mkundef(); - } - - ant_value_t ext_slot = get_slot(js, obj, SLOT_EXTENSIBLE); - if (vtype(ext_slot) != T_UNDEF && !js_truthy(js, ext_slot)) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to non-extensible object"); - return js_mkundef(); - } - - int need_length_update = 0; - unsigned long idx = 0; - - if (vtype(obj) == T_ARR && klen > 0 && key[0] >= '0' && key[0] <= '9') { - char *inner_endptr; - idx = strtoul(key, &inner_endptr, 10); - if (inner_endptr == key + klen) { - ant_offset_t inner_len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); - ant_offset_t inner_cur_len = 0; - if (inner_len_off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, inner_len_off)); - if (vtype(len_val) == T_NUM) inner_cur_len = (ant_offset_t) tod(len_val); - } - if (idx >= inner_cur_len) need_length_update = 1; - } + { + ant_value_t extensibility_error = check_object_extensibility(js, obj); + if (is_err(extensibility_error)) return extensibility_error; + if (extensibility_error == js_false) return v; } ant_value_t result = mkprop(js, obj, k, v, 0); - if (need_length_update) { - ant_offset_t inner_len_off = lkp_interned(js, obj, INTERN_LENGTH, 6); - ant_value_t inner_new_len = tov((double)(idx + 1)); - if (inner_len_off != 0) saveval(js, inner_len_off + sizeof(ant_offset_t) * 2, inner_new_len); else { - mkprop(js, obj, js->length_str, inner_new_len, 0); - } - } + if (is_err(result)) return result; + array_define_or_set_index(js, obj, key, (size_t)klen); - return result; + return v; } ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) { @@ -2933,7 +3263,7 @@ ant_value_t setprop_cstr(ant_t *js, ant_value_t obj, const char *key, size_t len ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t v) { obj = js_as_obj(obj); - if (is_proxy(js, obj)) { + if (is_proxy(obj)) { ant_value_t result = proxy_set(js, obj, key, klen, v); if (is_err(result)) return result; return v; @@ -2943,15 +3273,44 @@ ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size ant_offset_t existing = lkp(js, obj, key, klen); { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, klen); - if (desc) { - if (!desc->writable && !desc->has_setter) { + bool has_desc = false; + bool desc_writable = true; + bool desc_has_setter = false; + ant_value_t desc_setter = js_mkundef(); + ant_value_t as_obj = js_as_obj(obj); + ant_object_t *ptr = js_obj_ptr(as_obj); + const char *interned_key = intern_string(key, klen); + + if (ptr && ptr->shape && interned_key) { + int32_t slot = ant_shape_lookup_interned(ptr->shape, interned_key); + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot); + if (prop) { + has_desc = true; + desc_writable = (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0; + desc_has_setter = prop->has_setter != 0; + desc_setter = prop->setter; + } + } + } + + if (!has_desc && ptr && ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(as_obj, key, klen); + if (desc) { + has_desc = true; + desc_writable = desc->writable; + desc_has_setter = desc->has_setter; + desc_setter = desc->setter; + } + } + + if (has_desc) { + if (!desc_writable && !desc_has_setter) { if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "assignment to read-only property"); return v; } - if (desc->has_setter) { - ant_value_t setter = desc->setter; + if (desc_has_setter) { + ant_value_t setter = desc_setter; uint8_t setter_type = vtype(setter); if (setter_type == T_FUNC || setter_type == T_CFUNC) { ant_value_t result = sv_vm_call(js->vm, js, setter, obj, &v, 1, NULL, false); @@ -2964,27 +3323,22 @@ ant_value_t js_define_own_prop(ant_t *js, ant_value_t obj, const char *key, size if (existing > 0) { if (is_const_prop(js, existing)) return js_mkerr(js, "assignment to constant"); - saveval(js, existing + sizeof(ant_offset_t) * 2, v); - return mkval(T_PROP, existing); + js_saveval(js, existing, v); + array_define_or_set_index(js, obj, key, klen); + return v; } - if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to frozen object"); - return js_mkundef(); - } - if (js_truthy(js, get_slot(js, obj, SLOT_SEALED))) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to sealed object"); - return js_mkundef(); - } - ant_value_t ext_slot = get_slot(js, obj, SLOT_EXTENSIBLE); - if (vtype(ext_slot) != T_UNDEF && !js_truthy(js, ext_slot)) { - if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot add property to non-extensible object"); - return js_mkundef(); + { + ant_value_t extensibility_error = check_object_extensibility(js, obj); + if (is_err(extensibility_error)) return extensibility_error; + if (extensibility_error == js_false) return v; } ant_value_t k = js_mkstr(js, key, klen); if (is_err(k)) return k; - return mkprop(js, obj, k, v, 0); + ant_value_t created = mkprop(js, obj, k, v, 0); + if (!is_err(created)) array_define_or_set_index(js, obj, key, klen); + return is_err(created) ? created : v; } ant_value_t setprop_interned(ant_t *js, ant_value_t obj, const char *key, size_t len, ant_value_t v) { @@ -2999,126 +3353,90 @@ ant_value_t js_setprop_nonconfigurable(ant_t *js, ant_value_t obj, const char *k ant_value_t result = js_setprop(js, obj, k, v); if (is_err(result)) return result; - js_set_descriptor(js, obj, key, keylen, JS_DESC_W); + js_set_descriptor(js, js_as_obj(obj), key, keylen, JS_DESC_W); return result; } -#define SYM_HEAP_FIXED (sizeof(uint32_t) + sizeof(uint32_t) + sizeof(uintptr_t)) #define SYM_FLAG_GLOBAL 1u -#define SYM_REGISTRY_MAX 256 -static struct { +typedef struct sym_registry_entry { const char *key; - uint32_t sym_id; -} g_sym_registry[SYM_REGISTRY_MAX]; - -static size_t g_sym_registry_count = 0; -static size_t g_sym_all_count = 0; -static size_t g_sym_all_cap = 0; -static ant_value_t *g_sym_all = NULL; - -static void sym_table_add(ant_value_t sym) { - if (g_sym_all_count >= g_sym_all_cap) { - size_t new_cap = g_sym_all_cap ? g_sym_all_cap * 2 : 64; - ant_value_t *new_buf = realloc(g_sym_all, new_cap * sizeof(ant_value_t)); - if (!new_buf) return; - g_sym_all = new_buf; - g_sym_all_cap = new_cap; - } - g_sym_all[g_sym_all_count++] = sym; -} + ant_value_t sym; + UT_hash_handle hh; +} sym_registry_entry_t; ant_value_t js_mksym(ant_t *js, const char *desc) { - uint32_t id = (uint32_t)(++js->sym_counter); + uint32_t id = (uint32_t)(++js->sym.counter); size_t desc_len = (desc && *desc) ? strlen(desc) : 0; - size_t payload = SYM_HEAP_FIXED + (desc_len ? desc_len + 1 : 0); - - ant_offset_t ofs = js_alloc(js, payload + sizeof(ant_offset_t)); - if (ofs == (ant_offset_t)~0) return js_mkerr(js, "oom"); - - ant_offset_t header = (ant_offset_t)(payload << 4); - memcpy(&js->mem[ofs], &header, sizeof(header)); - - ant_offset_t p = ofs + sizeof(ant_offset_t); - memcpy(&js->mem[p], &id, sizeof(id)); - p += sizeof(uint32_t); - uint32_t flags = 0; - memcpy(&js->mem[p], &flags, sizeof(flags)); - p += sizeof(uint32_t); - uintptr_t key_ptr = 0; - memcpy(&js->mem[p], &key_ptr, sizeof(key_ptr)); - p += sizeof(uintptr_t); + size_t total = sizeof(ant_symbol_heap_t) + (desc_len ? desc_len + 1 : 0); + ant_symbol_heap_t *sym_ptr = (ant_symbol_heap_t *)js_type_alloc( + js, ANT_ALLOC_SYMBOL, total, _Alignof(ant_symbol_heap_t) + ); + if (!sym_ptr) return js_mkerr(js, "oom"); + sym_ptr->id = id; + sym_ptr->flags = 0; + sym_ptr->key = NULL; + sym_ptr->desc_len = (uint32_t)desc_len; if (desc_len) { - memcpy(&js->mem[p], desc, desc_len); - js->mem[p + desc_len] = '\0'; + memcpy(sym_ptr->desc, desc, desc_len); + sym_ptr->desc[desc_len] = '\0'; } - ant_value_t sym = mkval(T_SYMBOL, ofs); - sym_table_add(sym); - return sym; + return mkval(T_SYMBOL, (uintptr_t)sym_ptr); +} + +static inline ant_symbol_heap_t *sym_ptr(ant_value_t v) { + return (ant_symbol_heap_t *)(uintptr_t)vdata(v); } static inline uint32_t sym_get_id(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t)vdata(v); - uint32_t id; - memcpy(&id, &js->mem[ofs + sizeof(ant_offset_t)], sizeof(id)); - return id; + (void)js; + ant_symbol_heap_t *ptr = sym_ptr(v); + return ptr ? ptr->id : 0; } static inline uint32_t sym_get_flags(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t)vdata(v); - uint32_t flags; - memcpy(&flags, &js->mem[ofs + sizeof(ant_offset_t) + sizeof(uint32_t)], sizeof(flags)); - return flags; + (void)js; + ant_symbol_heap_t *ptr = sym_ptr(v); + return ptr ? ptr->flags : 0; } static inline uintptr_t sym_get_key_ptr(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t)vdata(v); - uintptr_t kp; - memcpy(&kp, &js->mem[ofs + sizeof(ant_offset_t) + sizeof(uint32_t) * 2], sizeof(kp)); - return kp; + (void)js; + ant_symbol_heap_t *ptr = sym_ptr(v); + return ptr ? (uintptr_t)ptr->key : 0; } static const char *sym_get_desc(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t)vdata(v); - ant_offset_t header; - memcpy(&header, &js->mem[ofs], sizeof(header)); - size_t payload = (size_t)(header >> 4); - if (payload <= SYM_HEAP_FIXED) return NULL; - return (const char *)&js->mem[ofs + sizeof(ant_offset_t) + SYM_HEAP_FIXED]; -} - -uint64_t inline js_sym_id(ant_value_t sym) { - // TODO: fix to not use the global runtime - ant_t *js = rt->js; - return sym_get_id(js, sym); + (void)js; + ant_symbol_heap_t *ptr = sym_ptr(v); + if (!ptr || ptr->desc_len == 0) return NULL; + return ptr->desc; } ant_value_t js_mksym_for(ant_t *js, const char *key) { const char *interned = intern_string(key, strlen(key)); - for (size_t i = 0; i < g_sym_registry_count; i++) { - if (g_sym_registry[i].key == interned) { - uint32_t id = g_sym_registry[i].sym_id; - if (id - 1 < g_sym_all_count) return g_sym_all[id - 1]; - } - } + sym_registry_entry_t *reg = js->sym.registry; + sym_registry_entry_t *found = NULL; + HASH_FIND_PTR(reg, &interned, found); + if (found) return found->sym; ant_value_t sym = js_mksym(js, key); if (is_err(sym)) return sym; - ant_offset_t ofs = (ant_offset_t)vdata(sym); - uint32_t flags = SYM_FLAG_GLOBAL; - memcpy(&js->mem[ofs + sizeof(ant_offset_t) + sizeof(uint32_t)], &flags, sizeof(flags)); - uintptr_t kp = (uintptr_t)interned; - memcpy(&js->mem[ofs + sizeof(ant_offset_t) + sizeof(uint32_t) * 2], &kp, sizeof(kp)); + ant_symbol_heap_t *ptr = sym_ptr(sym); + if (!ptr) return js_mkerr(js, "oom"); + ptr->flags |= SYM_FLAG_GLOBAL; + ptr->key = interned; - if (g_sym_registry_count < SYM_REGISTRY_MAX) { - uint32_t id = sym_get_id(js, sym); - g_sym_registry[g_sym_registry_count].key = interned; - g_sym_registry[g_sym_registry_count].sym_id = id; - g_sym_registry_count++; + sym_registry_entry_t *entry = ant_calloc(sizeof(sym_registry_entry_t)); + if (entry) { + entry->key = interned; + entry->sym = sym; + HASH_ADD_PTR(reg, key, entry); + js->sym.registry = reg; } return sym; @@ -3135,69 +3453,19 @@ const inline char *js_sym_desc(ant_t *js, ant_value_t sym) { return sym_get_desc(js, sym); } -ant_value_t sym_lookup_by_id(uint32_t id) { - if (id == 0 || id - 1 >= g_sym_all_count) return (ant_value_t)0; - return g_sym_all[id - 1]; -} - -void sym_gc_update_all(void (*op_val)(void *, ant_value_t *), void *ctx) { - for (size_t i = 0; i < g_sym_all_count; i++) { - op_val(ctx, &g_sym_all[i]); - } -} - -ant_offset_t esize(ant_offset_t w) { - ant_offset_t cleaned = w & ~FLAGMASK; - switch (cleaned & 3U) { - case T_OBJ: return (ant_offset_t) (sizeof(ant_offset_t) + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - case T_PROP: return (ant_offset_t) (sizeof(ant_offset_t) + sizeof(ant_offset_t) + sizeof(ant_value_t)); - case T_STR: return (ant_offset_t) (sizeof(ant_offset_t) + align64(cleaned >> 3U)); - default: return (ant_offset_t) ~0U; - } -} - static inline bool streq(const char *buf, size_t len, const char *s, size_t n) { return len == n && !memcmp(buf, s, n); } -static inline ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len) { +ant_offset_t lkp_interned(ant_t *js, ant_value_t obj, const char *search_intern, size_t len) { obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - ant_offset_t tail = loadoff(js, obj_off + sizeof(ant_offset_t) * 2); - - uint32_t slot = (((uintptr_t)search_intern >> 3) ^ obj_off) & (ANT_LIMIT_SIZE_CACHE - 1); - intern_prop_cache_entry_t *ce = &intern_prop_cache[slot]; - - if (ce->generation == intern_prop_cache_gen - && ce->obj_off == obj_off - && ce->intern_ptr == search_intern - && ce->tail == tail - ) return ce->prop_off; - - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - ant_offset_t off = first_prop; ant_offset_t result = 0; - - while (off < js->brk && off != 0) { - ant_offset_t header = loadoff(js, off); - if (is_slot_prop(header)) { off = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, off + sizeof(ant_offset_t)); - ant_offset_t klen = (loadoff(js, koff) >> 3) - 1; - - if (klen == len) { - const char *p = (char *)&js->mem[koff + sizeof(ant_offset_t)]; - if (intern_string(p, klen) == search_intern) { result = off; break; } - } - off = next_prop(header); - } - - ce->generation = intern_prop_cache_gen; - ce->obj_off = obj_off; - ce->intern_ptr = search_intern; - ce->prop_off = result; - ce->tail = tail; - - return result; + ant_object_t *ptr = js_obj_ptr(obj); + if (!search_intern || !ptr || !ptr->shape) return 0; + + int32_t shape_slot = ant_shape_lookup_interned(ptr->shape, search_intern); + (void)len; + if (shape_slot < 0) return 0; + return propref_make(js, ptr, (uint32_t)shape_slot); } inline ant_offset_t lkp(ant_t *js, ant_value_t obj, const char *buf, size_t len) { @@ -3206,82 +3474,154 @@ inline ant_offset_t lkp(ant_t *js, ant_value_t obj, const char *buf, size_t len) return lkp_interned(js, obj, search_intern, len); } +inline ant_value_t lkp_interned_val(ant_t *js, ant_value_t obj, const char *search_intern) { + obj = js_as_obj(obj); + ant_object_t *ptr = js_obj_ptr(obj); + if (!search_intern || !ptr || !ptr->shape) return js_mkundef(); + int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern); + if (slot < 0) return js_mkundef(); + return ant_object_prop_get_unchecked(ptr, (uint32_t)slot); +} + +static inline ant_value_t lkp_val(ant_t *js, ant_value_t obj, const char *buf, size_t len) { + const char *interned = intern_string(buf, len); + if (!interned) return js_mkundef(); + return lkp_interned_val(js, obj, interned); +} + ant_offset_t lkp_sym(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - ant_offset_t off = first_prop; - - while (off < js->brk && off != 0) { - ant_offset_t header = loadoff(js, off); - if (is_slot_prop(header)) { off = next_prop(header); continue; } - ant_offset_t koff = loadoff(js, off + sizeof(ant_offset_t)); - if (koff == sym_off) return off; - off = next_prop(header); - } - return 0; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return 0; + int32_t slot = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (slot < 0) return 0; + return propref_make(js, ptr, (uint32_t)slot); } ant_offset_t lkp_sym_proto(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { - for (int i = 0; i < MAX_PROTO_CHAIN_DEPTH; i++) { + ant_value_t cur = obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + while (is_object_type(cur)) { + obj = cur; ant_offset_t off = lkp_sym(js, obj, sym_off); if (off != 0) return off; - ant_value_t proto = get_proto(js, js_as_obj(obj)); + ant_value_t proto = get_proto(js, js_as_obj(cur)); if (!is_object_type(proto)) break; - obj = proto; + cur = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } return 0; } +static inline ant_value_t lkp_sym_proto_val(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { + ant_value_t cur = obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + + while (is_object_type(cur)) { + ant_value_t as_obj = js_as_obj(cur); + ant_object_t *ptr = js_obj_ptr(as_obj); + + if (ptr && ptr->shape) { + int32_t slot = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (slot >= 0) return ant_object_prop_get_unchecked(ptr, (uint32_t)slot); + } + + ant_value_t proto = get_proto(js, as_obj); + if (!is_object_type(proto)) break; + + cur = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; + } + + return js_mkundef(); +} -static ant_offset_t lkp_with_getter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *getter_out, bool *has_getter_out) { +static uintptr_t lkp_with_getter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *getter_out, bool *has_getter_out) { *has_getter_out = false; *getter_out = js_mkundef(); + const char *search_intern = intern_string(buf, len); - for (ant_value_t current = obj; is_object_type(current); ) { + ant_value_t current = obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + + while (is_object_type(current)) { current = js_as_obj(current); - ant_offset_t current_off = (ant_offset_t)vdata(current); - descriptor_entry_t *desc = lookup_descriptor(js, current_off, buf, len); - - if (desc && desc->has_getter) { - *getter_out = desc->getter; - *has_getter_out = true; - return current_off; + uintptr_t current_id = (uintptr_t)vdata(current); + + ant_object_t *ptr = js_obj_ptr(current); + if (ptr && ptr->shape && search_intern) { + int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern); + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot); + if (prop && prop->has_getter) { + *getter_out = prop->getter; + *has_getter_out = true; + return current_id; + } + return propref_make(js, ptr, (uint32_t)slot); + } + } + + if (ptr && ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(current, buf, len); + if (desc && desc->has_getter) { + *getter_out = desc->getter; + *has_getter_out = true; + return current_id; + } } - - ant_offset_t prop_off = lkp_interned(js, current, intern_string(buf, len), len); - if (prop_off != 0) return prop_off; ant_value_t proto = get_proto(js, current); if (!is_object_type(proto)) break; current = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, current)) break; } return 0; } -static ant_offset_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out) { +static uintptr_t lkp_with_setter(ant_t *js, ant_value_t obj, const char *buf, size_t len, ant_value_t *setter_out, bool *has_setter_out) { *has_setter_out = false; *setter_out = js_mkundef(); + const char *search_intern = intern_string(buf, len); ant_value_t current = obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); while (vtype(current) == T_OBJ || vtype(current) == T_FUNC) { current = js_as_obj(current); - ant_offset_t current_off = (ant_offset_t)vdata(current); - descriptor_entry_t *desc = lookup_descriptor(js, current_off, buf, len); - - if (desc && desc->has_setter) { - *setter_out = desc->setter; - *has_setter_out = true; - return current_off; + uintptr_t current_id = (uintptr_t)vdata(current); + + ant_object_t *ptr = js_obj_ptr(current); + if (ptr && ptr->shape && search_intern) { + int32_t slot = ant_shape_lookup_interned(ptr->shape, search_intern); + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, (uint32_t)slot); + if (prop && prop->has_setter) { + *setter_out = prop->setter; + *has_setter_out = true; + return current_id; + } + return propref_make(js, ptr, (uint32_t)slot); + } + } + + if (ptr && ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(current, buf, len); + if (desc && desc->has_setter) { + *setter_out = desc->setter; + *has_setter_out = true; + return current_id; + } } - - ant_offset_t prop_off = lkp_interned(js, current, intern_string(buf, len), len); - if (prop_off != 0) return prop_off; ant_value_t proto = get_proto(js, current); if (vtype(proto) != T_OBJ && vtype(proto) != T_FUNC) break; current = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, current)) break; } return 0; @@ -3311,8 +3651,9 @@ ant_value_t js_get_proto(ant_t *js, ant_value_t obj) { if (!is_object_type(obj)) return js_mknull(); ant_value_t as_obj = js_as_obj(obj); - - ant_value_t proto = get_slot(js, as_obj, SLOT_PROTO); + + ant_object_t *ptr = js_obj_ptr(as_obj); + ant_value_t proto = ptr ? ptr->proto : js_mkundef(); if (is_object_type(proto)) return proto; if (t != T_OBJ) return get_prototype_for_type(js, t); @@ -3323,26 +3664,41 @@ static ant_value_t get_proto(ant_t *js, ant_value_t obj) { return js_get_proto(js, obj); } -void js_set_proto(ant_t *js, ant_value_t obj, ant_value_t proto) { +void js_set_proto(ant_value_t obj, ant_value_t proto) { if (!is_object_type(obj)) return; - + ant_value_t as_obj = js_as_obj(obj); - set_slot(js, as_obj, SLOT_PROTO, proto); + ant_object_t *ptr = js_obj_ptr(as_obj); + if (!ptr) return; + + ptr->proto = proto; + ant_ic_epoch_bump(); +} + +void js_set_proto_init(ant_value_t obj, ant_value_t proto) { + if (!is_object_type(obj)) return; + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr) return; + ptr->proto = proto; } static void set_proto(ant_t *js, ant_value_t obj, ant_value_t proto) { - js_set_proto(js, obj, proto); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + js_set_proto(obj, proto); + if (ptr) gc_write_barrier(js, ptr, proto); +} + +void js_set_proto_wb(ant_t *js, ant_value_t obj, ant_value_t proto) { + set_proto(js, obj, proto); } ant_value_t js_get_ctor_proto(ant_t *js, const char *name, size_t len) { - ant_offset_t ctor_off = lkp_interned(js, js->global, intern_string(name, len), len); - if (ctor_off == 0) return js_mknull(); - ant_value_t ctor = resolveprop(js, mkval(T_PROP, ctor_off)); + const char *interned = intern_string(name, len); + ant_value_t ctor = lkp_interned_val(js, js->global, interned); if (vtype(ctor) != T_FUNC) return js_mknull(); ant_value_t ctor_obj = js_as_obj(ctor); - ant_offset_t proto_off = lkp_interned(js, ctor_obj, INTERN_PROTOTYPE, 9); - if (proto_off == 0) return js_mknull(); - return resolveprop(js, mkval(T_PROP, proto_off)); + ant_value_t proto = lkp_interned_val(js, ctor_obj, INTERN_PROTOTYPE); + return vtype(proto) == T_UNDEF ? js_mknull() : proto; } static inline ant_value_t get_ctor_proto(ant_t *js, const char *name, size_t len) { @@ -3369,9 +3725,10 @@ ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *key, size_t len) if (!key_intern) return 0; ant_value_t cur = obj; - int depth = 0; + proto_walk_overflow_guard_t guard; + proto_walk_overflow_guard_init(&guard); - while (depth < MAX_PROTO_CHAIN_DEPTH) { + while (true) { if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE) { ant_value_t as_obj = js_as_obj(cur); ant_offset_t off = lkp_interned(js, as_obj, key_intern, len); @@ -3385,7 +3742,9 @@ ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *key, size_t len) } break; } else if (t != T_STR && t != T_NUM && t != T_BOOL && t != T_BIGINT && t != T_SYMBOL) break; - if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_LOOKUP)) { break; } depth++; + + if (!proto_walk_next(js, &cur, &t, PROTO_WALK_F_LOOKUP)) break; + if (proto_walk_overflow_guard_hit_cycle(js, &guard, cur, t, PROTO_WALK_F_LOOKUP)) break; } return 0; @@ -3394,51 +3753,48 @@ ant_offset_t lkp_proto(ant_t *js, ant_value_t obj, const char *key, size_t len) static ant_value_t getprop_any(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { uint8_t t = vtype(obj); - if (t == T_STR && key_len == 6 && memcmp(key, "length", 6) == 0) { + if (t == T_STR && is_length_key(key, key_len)) { ant_offset_t byte_len; ant_offset_t str_off = vstr(js, obj, &byte_len); - return tov(D(utf16_strlen((const char *)&js->mem[str_off], byte_len))); + return tov(D(utf16_strlen((const char *)(uintptr_t)(str_off), byte_len))); } if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) { ant_offset_t off = lkp_proto(js, obj, key, key_len); - if (off != 0) return resolveprop(js, mkval(T_PROP, off)); + if (off != 0) return propref_load(js, off); return js_mkundef(); } if (t == T_OBJ || t == T_ARR || t == T_FUNC) { ant_value_t as_obj = js_as_obj(obj); ant_offset_t off = lkp(js, as_obj, key, key_len); - if (off != 0) return resolveprop(js, mkval(T_PROP, off)); + if (off != 0) return propref_load(js, off); off = lkp_proto(js, obj, key, key_len); - if (off != 0) return resolveprop(js, mkval(T_PROP, off)); + if (off != 0) return propref_load(js, off); } return js_mkundef(); } static ant_value_t try_dynamic_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry || !entry->getter) return js_mkundef(); - return entry->getter(js, obj, key, key_len); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr || !ptr->is_exotic) return js_mkundef(); + if (!ptr->exotic_ops || !ptr->exotic_ops->getter) return js_mkundef(); + return ptr->exotic_ops->getter(js, obj, key, key_len); } static bool try_dynamic_setter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t value) { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry || !entry->setter) return false; - return entry->setter(js, obj, key, key_len, value); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr || !ptr->is_exotic) return false; + if (!ptr->exotic_ops || !ptr->exotic_ops->setter) return false; + return ptr->exotic_ops->setter(js, obj, key, key_len, value); } static bool try_dynamic_deleter(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry || !entry->deleter) return false; - return entry->deleter(js, obj, key, key_len); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr || !ptr->is_exotic) return false; + if (!ptr->exotic_ops || !ptr->exotic_ops->deleter) return false; + return ptr->exotic_ops->deleter(js, obj, key, key_len); } static bool try_accessor_getter(ant_t *js, ant_value_t obj, const char *key, size_t key_len, ant_value_t *out) { @@ -3471,9 +3827,8 @@ static bool try_accessor_setter(ant_t *js, ant_value_t obj, const char *key, siz return true; } -ant_value_t resolveprop(ant_t *js, ant_value_t v) { - if (vtype(v) != T_PROP) return v; - return resolveprop(js, loadval(js, (ant_offset_t) (vdata(v) + sizeof(ant_offset_t) * 2))); +ant_value_t js_propref_load(ant_t *js, ant_offset_t handle) { + return propref_load(js, handle); } typedef struct { @@ -3522,7 +3877,7 @@ static ant_value_t string_builder_finalize(ant_t *js, string_builder_t *sb) { ant_offset_t str_len_fast(ant_t *js, ant_value_t str) { if (vtype(str) != T_STR) return 0; - if (is_rope(js, str)) return rope_len(js, str); + if (str_is_heap_rope(str)) return rope_len(str); return assert_flat_string_len(js, str, NULL); } @@ -3535,15 +3890,15 @@ ant_value_t do_string_op(ant_t *js, uint8_t op, ant_value_t l, ant_value_t r) { if (n2 == 0) return l; if (n1 == 0) return r; - uint8_t left_depth = (vtype(l) == T_STR && is_rope(js, l)) ? rope_depth(js, l) : 0; - uint8_t right_depth = (vtype(r) == T_STR && is_rope(js, r)) ? rope_depth(js, r) : 0; + uint8_t left_depth = (vtype(l) == T_STR && str_is_heap_rope(l)) ? rope_depth(l) : 0; + uint8_t right_depth = (vtype(r) == T_STR && str_is_heap_rope(r)) ? rope_depth(r) : 0; uint8_t new_depth = (left_depth > right_depth ? left_depth : right_depth) + 1; if (new_depth >= ROPE_MAX_DEPTH || total_len >= ROPE_FLATTEN_THRESHOLD) { ant_value_t flat_l = l, flat_r = r; - if (is_rope(js, l)) flat_l = rope_flatten(js, l); + if (str_is_heap_rope(l)) flat_l = rope_flatten(js, l); if (is_err(flat_l)) return flat_l; - if (is_rope(js, r)) flat_r = rope_flatten(js, r); + if (str_is_heap_rope(r)) flat_r = rope_flatten(js, r); if (is_err(flat_r)) return flat_r; ant_offset_t off1, off2, len1, len2; @@ -3555,8 +3910,8 @@ ant_value_t do_string_op(ant_t *js, uint8_t op, ant_value_t l, ant_value_t r) { string_builder_init(&sb, static_buffer, sizeof(static_buffer)); if ( - !string_builder_append(&sb, (char *)&js->mem[off1], len1) || - !string_builder_append(&sb, (char *)&js->mem[off2], len2) + !string_builder_append(&sb, (char *)(uintptr_t)(off1), len1) || + !string_builder_append(&sb, (char *)(uintptr_t)(off2), len2) ) return js_mkerr(js, "string concatenation failed"); return string_builder_finalize(js, &sb); @@ -3569,14 +3924,16 @@ ant_value_t do_string_op(ant_t *js, uint8_t op, ant_value_t l, ant_value_t r) { ant_offset_t n2, off2 = vstr(js, r, &n2); if (op == TOK_EQ) { - bool eq = n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0; + bool eq = n1 == n2 && + memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0; return mkval(T_BOOL, eq ? 1 : 0); } else if (op == TOK_NE) { - bool eq = n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0; + bool eq = n1 == n2 && + memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0; return mkval(T_BOOL, eq ? 0 : 1); } else if (op == TOK_LT || op == TOK_LE || op == TOK_GT || op == TOK_GE) { ant_offset_t min_len = n1 < n2 ? n1 : n2; - int cmp = memcmp(&js->mem[off1], &js->mem[off2], min_len); + int cmp = memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, min_len); if (cmp == 0) { if (n1 == n2) { @@ -3602,14 +3959,14 @@ static ant_value_t iter_foreach(ant_t *js, ant_value_t iterable, iter_callback_t static bool js_try_call_method(ant_t *js, ant_value_t obj, const char *method, size_t method_len, ant_value_t *args, int nargs, ant_value_t *out_result) { ant_value_t getter = js_mkundef(); bool has_getter = false; - ant_offset_t off = lkp_with_getter(js, obj, method, method_len, &getter, &has_getter); + uintptr_t off = lkp_with_getter(js, obj, method, method_len, &getter, &has_getter); ant_value_t fn; if (has_getter) { fn = call_proto_accessor(js, obj, getter, true, NULL, 0, false); if (is_err(fn)) { *out_result = fn; return true; } } else if (off != 0) { - fn = resolveprop(js, mkval(T_PROP, off)); + fn = propref_load(js, (ant_offset_t)off); } else return false; uint8_t ft = vtype(fn); @@ -3681,7 +4038,7 @@ static ant_value_t try_exotic_to_primitive(ant_t *js, ant_value_t value, int hin ant_offset_t tp_off = lkp_sym_proto(js, value, (ant_offset_t)vdata(tp_sym)); if (tp_off == 0) return mkval(T_UNDEF, 0); - ant_value_t tp_fn = resolveprop(js, mkval(T_PROP, tp_off)); + ant_value_t tp_fn = propref_load(js, tp_off); uint8_t ft = vtype(tp_fn); if (ft == T_UNDEF) return mkval(T_UNDEF, 0); @@ -3728,7 +4085,8 @@ bool strict_eq_values(ant_t *js, ant_value_t l, ant_value_t r) { if (t != vtype(r)) return false; if (t == T_STR) { ant_offset_t n1, n2, off1 = vstr(js, l, &n1), off2 = vstr(js, r, &n2); - return n1 == n2 && memcmp(&js->mem[off1], &js->mem[off2], n1) == 0; + return n1 == n2 && + memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, n1) == 0; } if (t == T_NUM) return tod(l) == tod(r); if (t == T_BIGINT) return bigint_compare(js, l, r) == 0; @@ -3761,28 +4119,15 @@ ant_value_t coerce_to_str_concat(ant_t *js, ant_value_t v) { return js_tostring_val(js, v); } -static void unlink_prop(ant_t *js, ant_offset_t obj_off, ant_offset_t prop_off, ant_offset_t prev_off) { - ant_offset_t deleted_next = loadoff(js, prop_off) & ~FLAGMASK; - ant_offset_t target = prev_off ? prev_off : obj_off; - ant_offset_t current = loadoff(js, target); - - saveoff(js, target, (deleted_next & ~3ULL) | (current & (FLAGMASK | 3ULL))); - ant_offset_t tail = loadoff(js, obj_off + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - - if (tail == prop_off) { - saveoff(js, obj_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), prev_off); - } - - invalidate_prop_cache(js, obj_off, prop_off); - js->needs_gc = true; -} - static ant_value_t check_frozen_sealed(ant_t *js, ant_value_t obj, const char *action) { - if (js_truthy(js, get_slot(js, obj, SLOT_FROZEN))) { + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr) return js_mkundef(); + + if (ptr->frozen) { if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot %s property of frozen object", action); return js_false; } - if (js_truthy(js, get_slot(js, obj, SLOT_SEALED))) { + if (ptr->sealed) { if (sv_vm_is_strict(js->vm)) return js_mkerr(js, "cannot %s property of sealed object", action); return js_false; } @@ -3790,8 +4135,11 @@ static ant_value_t check_frozen_sealed(ant_t *js, ant_value_t obj, const char *a } ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t len) { + ant_value_t original_obj = obj; obj = js_as_obj(obj); - if (is_proxy(js, obj)) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return js_true; + if (is_proxy(obj)) { ant_value_t result = proxy_delete(js, obj, key, len); return is_err(result) ? result : js_bool(js_truthy(js, result)); } @@ -3799,53 +4147,61 @@ ant_value_t js_delete_prop(ant_t *js, ant_value_t obj, const char *key, size_t l ant_value_t err = check_frozen_sealed(js, obj, "delete"); if (vtype(err) != T_UNDEF) return err; - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(obj)); + if (array_obj_ptr(original_obj) && is_length_key(key, len)) { + if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); + return js_false; + } - if (is_arr_off(js, obj_off)) { - ant_offset_t doff = get_dense_buf_off(js, obj_off); - unsigned long del_idx; - if (doff && parse_array_index(key, len, dense_length(js, doff), &del_idx)) { - arr_del(js, mkval(T_ARR, (uint64_t)obj_off), (ant_offset_t)del_idx); - return js_true; + if (array_obj_ptr(original_obj)) { + ant_offset_t doff = get_dense_buf(original_obj); + unsigned long del_idx = 0; + if (doff && parse_array_index(key, len, get_array_length(js, original_obj), &del_idx)) { + ant_offset_t dense_len = dense_iterable_length(js, original_obj); + if ((ant_offset_t)del_idx < dense_len) dense_set(js, doff, (ant_offset_t)del_idx, T_EMPTY); } } - ant_offset_t prop_off = lkp(js, obj, key, len); - if (prop_off == 0) { + const char *interned = intern_string(key, len); + if (!interned) { try_dynamic_deleter(js, obj, key, len); return js_true; } - if (is_nonconfig_prop(js, prop_off)) { - if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); - return js_false; + int32_t shape_slot = ant_shape_lookup_interned(ptr->shape, interned); + if (shape_slot < 0) { + try_dynamic_deleter(js, obj, key, len); + return js_true; } - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, len); - if (desc && !desc->configurable) { + uint8_t attrs = ant_shape_get_attrs(ptr->shape, (uint32_t)shape_slot); + if ((attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0) { if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); return js_false; } - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - if (first_prop == prop_off) { - unlink_prop(js, obj_off, prop_off, 0); - return js_true; - } - for (ant_offset_t prev = first_prop; prev != 0; ) { - ant_offset_t next_prop = loadoff(js, prev) & ~(3U | FLAGMASK); - if (next_prop == prop_off) { - unlink_prop(js, obj_off, prop_off, prev); - return js_true; + if (ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(obj, key, len); + if (desc && !desc->configurable) { + if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); + return js_false; } - prev = next_prop; } + + uint32_t slot = (uint32_t)shape_slot; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + uint32_t swapped_from = slot; + if (!ant_shape_remove_slot(ptr->shape, slot, &swapped_from)) return js_true; + + obj_remove_prop_slot(ptr, slot); + propref_adjust_after_swap_delete(js, ptr, slot, swapped_from); return js_true; } ant_value_t js_delete_sym_prop(ant_t *js, ant_value_t obj, ant_value_t sym) { obj = js_as_obj(obj); - if (is_proxy(js, obj)) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return js_true; + if (is_proxy(obj)) { ant_value_t result = proxy_delete_val(js, obj, sym); return is_err(result) ? result : js_bool(js_truthy(js, result)); } @@ -3853,29 +4209,25 @@ ant_value_t js_delete_sym_prop(ant_t *js, ant_value_t obj, ant_value_t sym) { ant_value_t err = check_frozen_sealed(js, obj, "delete"); if (vtype(err) != T_UNDEF) return err; - ant_offset_t obj_off = (ant_offset_t)vdata(obj); ant_offset_t sym_off = (ant_offset_t)vdata(sym); - ant_offset_t prop_off = lkp_sym(js, obj, sym_off); - if (prop_off == 0) return js_true; + int32_t shape_slot = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (shape_slot < 0) return js_true; - if (is_nonconfig_prop(js, prop_off)) { + uint8_t attrs = ant_shape_get_attrs(ptr->shape, (uint32_t)shape_slot); + if ((attrs & ANT_PROP_ATTR_CONFIGURABLE) == 0) { if (sv_vm_is_strict(js->vm)) return js_mkerr_typed(js, JS_ERR_TYPE, "cannot delete non-configurable property"); return js_false; } - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - if (first_prop == prop_off) { - unlink_prop(js, obj_off, prop_off, 0); - return js_true; - } - for (ant_offset_t prev = first_prop; prev != 0; ) { - ant_offset_t np = loadoff(js, prev) & ~(3U | FLAGMASK); - if (np == prop_off) { - unlink_prop(js, obj_off, prop_off, prev); - return js_true; - } - prev = np; - } + uint32_t slot = (uint32_t)shape_slot; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + + uint32_t swapped_from = slot; + if (!ant_shape_remove_slot(ptr->shape, slot, &swapped_from)) return js_true; + + obj_remove_prop_slot(ptr, slot); + propref_adjust_after_swap_delete(js, ptr, slot, swapped_from); + return js_true; } @@ -3887,7 +4239,7 @@ static ant_value_t iter_call_noargs_with_this(ant_t *js, ant_value_t this_val, a static ant_value_t iter_close_iterator(ant_t *js, ant_value_t iterator) { ant_offset_t return_off = lkp_proto(js, iterator, "return", 6); if (return_off == 0) return js_mkundef(); - ant_value_t return_method = loadval(js, return_off + sizeof(ant_offset_t) * 2); + ant_value_t return_method = propref_load(js, return_off); if (vtype(return_method) != T_FUNC && vtype(return_method) != T_CFUNC) { return js_mkerr(js, "iterator.return is not a function"); } @@ -3899,50 +4251,44 @@ static ant_value_t iter_foreach(ant_t *js, ant_value_t iterable, iter_callback_t ant_offset_t iter_prop = (vtype(iter_sym) == T_SYMBOL) ? lkp_sym_proto(js, iterable, (ant_offset_t)vdata(iter_sym)) : 0; if (iter_prop == 0) return js_mkerr(js, "not iterable"); - ant_value_t iter_method = loadval(js, iter_prop + sizeof(ant_offset_t) * 2); + ant_value_t iter_method = propref_load(js, iter_prop); ant_value_t iterator = iter_call_noargs_with_this(js, iterable, iter_method); if (is_err(iterator)) return iterator; - ant_handle_t h_iterator = js_root(js, iterator); ant_value_t out = js_mkundef(); while (true) { - ant_value_t cur_iter = js_deref(js, h_iterator); - ant_offset_t next_off = lkp_proto(js, cur_iter, "next", 4); - if (next_off == 0) { js_unroot(js, h_iterator); return js_mkerr(js, "iterator.next is not a function"); } + ant_offset_t next_off = lkp_proto(js, iterator, "next", 4); + if (next_off == 0) { return js_mkerr(js, "iterator.next is not a function"); } - ant_value_t next_method = loadval(js, next_off + sizeof(ant_offset_t) * 2); + ant_value_t next_method = propref_load(js, next_off); if (vtype(next_method) != T_FUNC && vtype(next_method) != T_CFUNC) { - js_unroot(js, h_iterator); return js_mkerr(js, "iterator.next is not a function"); } - cur_iter = js_deref(js, h_iterator); - ant_value_t result = iter_call_noargs_with_this(js, cur_iter, next_method); - if (is_err(result)) { js_unroot(js, h_iterator); return result; } + ant_value_t result = iter_call_noargs_with_this(js, iterator, next_method); + if (is_err(result)) { return result; } ant_offset_t done_off = lkp(js, result, "done", 4); - ant_value_t done_val = done_off ? loadval(js, done_off + sizeof(ant_offset_t) * 2) : js_mkundef(); + ant_value_t done_val = done_off ? propref_load(js, done_off) : js_mkundef(); if (js_truthy(js, done_val)) break; ant_offset_t value_off = lkp(js, result, "value", 5); - ant_value_t value = value_off ? loadval(js, value_off + sizeof(ant_offset_t) * 2) : js_mkundef(); + ant_value_t value = value_off ? propref_load(js, value_off) : js_mkundef(); iter_action_t action = cb(js, value, ctx, &out); if (action == ITER_BREAK) { - ant_value_t close_result = iter_close_iterator(js, cur_iter); - if (is_err(close_result)) { js_unroot(js, h_iterator); return close_result; } + ant_value_t close_result = iter_close_iterator(js, iterator); + if (is_err(close_result)) { return close_result; } break; } if (action == ITER_ERROR) { - ant_value_t close_result = iter_close_iterator(js, cur_iter); - js_unroot(js, h_iterator); + ant_value_t close_result = iter_close_iterator(js, iterator); if (is_err(close_result)) return close_result; return out; } } - js_unroot(js, h_iterator); return out; } @@ -3984,11 +4330,11 @@ static ant_value_t builtin_String(ant_t *js, ant_value_t *args, int nargs) { ant_value_t string_proto = js_get_ctor_proto(js, "String", 6); if (is_wrapper_ctor_target(js, js->this_val, string_proto)) { - set_slot(js, js->this_val, SLOT_PRIMITIVE, sval); + set_slot(js->this_val, SLOT_PRIMITIVE, sval); ant_offset_t slen; vstr(js, sval, &slen); js_setprop(js, js->this_val, js->length_str, tov((double)slen)); - js_set_descriptor(js, js->this_val, "length", 6, 0); + js_set_descriptor(js, js_as_obj(js->this_val), "length", 6, 0); } return sval; } @@ -4031,7 +4377,7 @@ static ant_value_t builtin_eval(ant_t *js, ant_value_t *args, int nargs) { if (vtype(code) != T_STR) return code; ant_offset_t code_len = 0; ant_offset_t code_off = vstr(js, code, &code_len); - const char *code_str = (const char *)&js->mem[code_off]; + const char *code_str = (const char *)(uintptr_t)(code_off); return js_eval_bytecode_eval_with_strict(js, code_str, (size_t)code_len, false); } @@ -4063,7 +4409,7 @@ static ant_value_t builtin_Number(ant_t *js, ant_value_t *args, int nargs) { ant_value_t nval = tov(nargs > 0 ? js_to_number(js, args[0]) : 0.0); ant_value_t number_proto = js_get_ctor_proto(js, "Number", 6); if (is_wrapper_ctor_target(js, js->this_val, number_proto)) { - set_slot(js, js->this_val, SLOT_PRIMITIVE, nval); + set_slot(js->this_val, SLOT_PRIMITIVE, nval); } return nval; } @@ -4072,7 +4418,7 @@ static ant_value_t builtin_Boolean(ant_t *js, ant_value_t *args, int nargs) { ant_value_t bval = mkval(T_BOOL, nargs > 0 && js_truthy(js, args[0]) ? 1 : 0); ant_value_t boolean_proto = js_get_ctor_proto(js, "Boolean", 7); if (is_wrapper_ctor_target(js, js->this_val, boolean_proto)) { - set_slot(js, js->this_val, SLOT_PRIMITIVE, bval); + set_slot(js->this_val, SLOT_PRIMITIVE, bval); } return bval; } @@ -4091,9 +4437,9 @@ static ant_value_t builtin_Object(ant_t *js, ant_value_t *args, int nargs) { if (t == T_STR || t == T_NUM || t == T_BOOL || t == T_BIGINT) { ant_value_t wrapper = js_mkobj(js); if (is_err(wrapper)) return wrapper; - set_slot(js, wrapper, SLOT_PRIMITIVE, arg); + set_slot(wrapper, SLOT_PRIMITIVE, arg); ant_value_t proto = get_prototype_for_type(js, t); - if (vtype(proto) == T_OBJ) set_proto(js, wrapper, proto); + if (vtype(proto) == T_OBJ) js_set_proto_init(wrapper, proto); return wrapper; } @@ -4109,15 +4455,15 @@ static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int narg set_func_code_ptr(js, func_obj, "(){}", 4); if (is_async) { - set_slot(js, func_obj, SLOT_ASYNC, js_true); - ant_value_t async_proto = get_slot(js, js_glob(js), SLOT_ASYNC_PROTO); - if (vtype(async_proto) == T_FUNC) set_proto(js, func_obj, async_proto); + set_slot(func_obj, SLOT_ASYNC, js_true); + ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); + if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); } else { - ant_value_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); + ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto); - if (is_object_type(instance_proto)) set_proto(js, func_obj, instance_proto); + if (is_object_type(instance_proto)) js_set_proto_init(func_obj, instance_proto); } - set_slot(js, func_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty)); + set_slot(func_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty)); ant_value_t func = js_obj_to_func(func_obj); ant_value_t proto_setup = setup_func_prototype(js, func); @@ -4148,14 +4494,14 @@ static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int narg code_buf[pos++] = '('; for (int i = 0; i < nargs - 1; i++) { ant_offset_t param_len, param_off = vstr(js, args[i], ¶m_len); - memcpy(code_buf + pos, &js->mem[param_off], param_len); + memcpy(code_buf + pos, (const void *)(uintptr_t)param_off, param_len); pos += param_len; if (i < nargs - 2) code_buf[pos++] = ','; } code_buf[pos++] = ')'; code_buf[pos++] = '{'; ant_offset_t body_len, body_off = vstr(js, body, &body_len); - memcpy(code_buf + pos, &js->mem[body_off], body_len); + memcpy(code_buf + pos, (const void *)(uintptr_t)body_off, body_len); pos += body_len; code_buf[pos++] = '}'; code_buf[pos] = '\0'; @@ -4169,7 +4515,8 @@ static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int narg return js_mkerr_typed(js, JS_ERR_SYNTAX, "invalid function body"); } - sv_closure_t *closure = calloc(1, sizeof(sv_closure_t)); + sv_closure_t *closure = js_closure_alloc(js); + if (!closure) { free(code_buf); return js_mkerr(js, "oom"); } closure->func = compiled; closure->bound_this = js_mkundef(); closure->call_flags = 0; @@ -4195,15 +4542,15 @@ static ant_value_t build_dynamic_function(ant_t *js, ant_value_t *args, int narg free(code_buf); if (is_async) { - set_slot(js, func_obj, SLOT_ASYNC, js_true); - ant_value_t async_proto = get_slot(js, js_glob(js), SLOT_ASYNC_PROTO); - if (vtype(async_proto) == T_FUNC) set_proto(js, func_obj, async_proto); + set_slot(func_obj, SLOT_ASYNC, js_true); + ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); + if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); } else { - ant_value_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); + ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); ant_value_t instance_proto = js_instance_proto_from_new_target(js, func_proto); - if (is_object_type(instance_proto)) set_proto(js, func_obj, instance_proto); + if (is_object_type(instance_proto)) js_set_proto_init(func_obj, instance_proto); } - + ant_value_t func = mkval(T_FUNC, (uintptr_t)closure); ant_value_t proto_setup = setup_func_prototype(js, func); if (is_err(proto_setup)) return proto_setup; @@ -4266,7 +4613,7 @@ static ant_value_t builtin_function_toString(ant_t *js, ant_value_t *args, int n if (t == T_CFUNC) return ANT_STRING("function() { [native code] }"); ant_value_t func_obj = js_func_obj(func); - ant_value_t cfunc_slot = get_slot(js, func_obj, SLOT_CFUNC); + ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); if (vtype(cfunc_slot) == T_CFUNC) { ant_offset_t name_len = 0; @@ -4285,15 +4632,15 @@ static ant_value_t builtin_function_toString(ant_t *js, ant_value_t *args, int n return ANT_STRING("function() { [native code] }"); } - ant_value_t code_val = get_slot(js, func_obj, SLOT_CODE); - ant_value_t len_val = get_slot(js, func_obj, SLOT_CODE_LEN); + ant_value_t code_val = get_slot(func_obj, SLOT_CODE); + ant_value_t len_val = get_slot(func_obj, SLOT_CODE_LEN); if (vtype(code_val) == T_CFUNC && vtype(len_val) == T_NUM) { const char *code = (const char *)(uintptr_t)vdata(code_val); size_t code_len = (size_t)tod(len_val); if (code && code_len > 0) { - ant_value_t async_slot = get_slot(js, func_obj, SLOT_ASYNC); + ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC); sv_closure_t *closure = js_func_closure(func); bool is_async = (async_slot == js_true); @@ -4383,12 +4730,9 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs orig_length = 0; } else { target_func_obj = js_func_obj(func); - ant_offset_t len_off = lkp_interned(js, target_func_obj, INTERN_LENGTH, 6); - if (len_off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, len_off)); - if (vtype(len_val) == T_NUM) { - orig_length = (int) tod(len_val); - } + ant_value_t len_val = lkp_interned_val(js, target_func_obj, INTERN_LENGTH); + if (vtype(len_val) == T_NUM) { + orig_length = (int) tod(len_val); } } @@ -4399,11 +4743,11 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs ant_value_t bound_func = mkobj(js, 0); if (is_err(bound_func)) return bound_func; - set_slot(js, bound_func, SLOT_CFUNC, func); - - ant_value_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); - if (vtype(func_proto) == T_FUNC) set_proto(js, bound_func, func_proto); + set_slot(bound_func, SLOT_CFUNC, func); + ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); + if (vtype(func_proto) == T_FUNC) js_set_proto_init(bound_func, func_proto); + ant_value_t bound = js_obj_to_func_ex(bound_func, bound_argc > 0 ? SV_CALL_HAS_BOUND_ARGS : 0); sv_closure_t *bc = js_func_closure(bound); bc->bound_this = this_arg; @@ -4416,6 +4760,8 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs ant_value_t proto_setup = setup_func_prototype(js, bound); if (is_err(proto_setup)) return proto_setup; + + js_mark_constructor(bound_func, js_is_constructor(js, func)); return bound; } @@ -4424,14 +4770,15 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs ant_value_t bound_func = mkobj(js, 0); if (is_err(bound_func)) return bound_func; - ant_value_t code_val = get_slot(js, func_obj, SLOT_CODE); + ant_value_t code_val = get_slot(func_obj, SLOT_CODE); if (vtype(code_val) == T_STR || vtype(code_val) == T_CFUNC) { - set_slot(js, bound_func, SLOT_CODE, code_val); - set_slot(js, bound_func, SLOT_CODE_LEN, get_slot(js, func_obj, SLOT_CODE_LEN)); + set_slot(bound_func, SLOT_CODE, code_val); + set_slot(bound_func, SLOT_CODE_LEN, get_slot(func_obj, SLOT_CODE_LEN)); } sv_closure_t *orig = js_func_closure(func); - sv_closure_t *bound_closure = calloc(1, sizeof(sv_closure_t)); + sv_closure_t *bound_closure = js_closure_alloc(js); + if (!bound_closure) return js_mkerr(js, "oom"); bound_closure->func = orig->func; bound_closure->upvalues = orig->upvalues; bound_closure->bound_this = this_arg; @@ -4442,28 +4789,28 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs if (bound_argc > 0) bound_closure->call_flags |= SV_CALL_HAS_BOUND_ARGS; - ant_value_t async_slot = get_slot(js, func_obj, SLOT_ASYNC); + ant_value_t async_slot = get_slot(func_obj, SLOT_ASYNC); if (vtype(async_slot) == T_BOOL && vdata(async_slot) == 1) { - set_slot(js, bound_func, SLOT_ASYNC, js_true); + set_slot(bound_func, SLOT_ASYNC, js_true); } ant_value_t target_proto = get_proto(js, func); if (is_object_type(target_proto)) { - set_proto(js, bound_func, target_proto); + js_set_proto_init(bound_func, target_proto); } else if (vtype(async_slot) == T_BOOL && vdata(async_slot) == 1) { - ant_value_t async_proto = get_slot(js, js_glob(js), SLOT_ASYNC_PROTO); - if (vtype(async_proto) == T_FUNC) set_proto(js, bound_func, async_proto); + ant_value_t async_proto = get_slot(js_glob(js), SLOT_ASYNC_PROTO); + if (vtype(async_proto) == T_FUNC) js_set_proto_init(bound_func, async_proto); } else { - ant_value_t func_proto = get_slot(js, js_glob(js), SLOT_FUNC_PROTO); - if (vtype(func_proto) == T_FUNC) set_proto(js, bound_func, func_proto); + ant_value_t func_proto = get_slot(js_glob(js), SLOT_FUNC_PROTO); + if (vtype(func_proto) == T_FUNC) js_set_proto_init(bound_func, func_proto); } - ant_value_t data_slot = get_slot(js, func_obj, SLOT_DATA); + ant_value_t data_slot = get_slot(func_obj, SLOT_DATA); if (vtype(data_slot) != T_UNDEF) { - set_slot(js, bound_func, SLOT_DATA, data_slot); + set_slot(bound_func, SLOT_DATA, data_slot); } - set_slot(js, bound_func, SLOT_TARGET_FUNC, func); + set_slot(bound_func, SLOT_TARGET_FUNC, func); if (bound_argc > 0) { ant_value_t bound_arr = mkarr(js); @@ -4474,9 +4821,9 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs bound_closure->bound_argc = bound_argc; } - ant_value_t cfunc_slot = get_slot(js, func_obj, SLOT_CFUNC); + ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); if (vtype(cfunc_slot) == T_CFUNC) { - set_slot(js, bound_func, SLOT_CFUNC, cfunc_slot); + set_slot(bound_func, SLOT_CFUNC, cfunc_slot); } js_setprop(js, bound_func, js->length_str, tov((double) bound_length)); @@ -4484,6 +4831,8 @@ static ant_value_t builtin_function_bind(ant_t *js, ant_value_t *args, int nargs ant_value_t bound = mkval(T_FUNC, (uintptr_t)bound_closure); ant_value_t proto_setup = setup_func_prototype(js, bound); if (is_err(proto_setup)) return proto_setup; + + js_mark_constructor(bound_func, js_is_constructor(js, func)); return bound; } @@ -4496,12 +4845,11 @@ static ant_value_t builtin_Array(ant_t *js, ant_value_t *args, int nargs) { ant_value_t err = validate_array_length(js, args[0]); if (is_err(err)) return err; ant_offset_t new_len = (ant_offset_t)tod(args[0]); - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff && new_len <= 1024) { - if (new_len > dense_capacity(js, doff)) doff = dense_grow(js, arr, new_len); - if (doff) dense_set_length(js, doff, new_len); + if (new_len > dense_capacity(doff)) doff = dense_grow(js, arr, new_len); } - update_array_length(js, arr, new_len); + array_len_set(js, arr, new_len); } else if (nargs > 0) { for (int i = 0; i < nargs; i++) arr_set(js, arr, (ant_offset_t)i, args[i]); } @@ -4509,9 +4857,9 @@ static ant_value_t builtin_Array(ant_t *js, ant_value_t *args, int nargs) { ant_value_t array_proto = get_ctor_proto(js, "Array", 5); ant_value_t instance_proto = js_instance_proto_from_new_target(js, array_proto); - if (is_object_type(instance_proto)) set_proto(js, arr, instance_proto); + if (is_object_type(instance_proto)) js_set_proto_init(arr, instance_proto); if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - set_slot(js, arr, SLOT_CTOR, js->new_target); + set_slot(arr, SLOT_CTOR, js->new_target); } return arr; @@ -4525,15 +4873,15 @@ static ant_value_t builtin_Error(ant_t *js, ant_value_t *args, int nargs) { ant_value_t name = ANT_STRING("Error"); if (vtype(target) == T_FUNC) { - ant_offset_t off = lkp(js, js_func_obj(target), "name", 4); - if (off) name = resolveprop(js, mkval(T_PROP, off)); + ant_value_t n = lkp_val(js, js_func_obj(target), "name", 4); + if (vtype(n) != T_UNDEF) name = n; } if (!is_new) { this_val = js_mkobj(js); - ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), INTERN_PROTOTYPE, 9); - if (proto_off) set_proto(js, this_val, resolveprop(js, mkval(T_PROP, proto_off))); - else set_proto(js, this_val, get_ctor_proto(js, "Error", 5)); + ant_value_t proto = lkp_interned_val(js, js_func_obj(js->current_func), INTERN_PROTOTYPE); + if (vtype(proto) != T_UNDEF) js_set_proto_init(this_val, proto); + else js_set_proto_init(this_val, get_ctor_proto(js, "Error", 5)); } if (nargs > 0) { @@ -4547,11 +4895,12 @@ static ant_value_t builtin_Error(ant_t *js, ant_value_t *args, int nargs) { if (nargs > 1 && vtype(args[1]) == T_OBJ) { ant_offset_t cause_off = lkp(js, args[1], "cause", 5); - if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, resolveprop(js, mkval(T_PROP, cause_off))); + if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, propref_load(js, cause_off)); } js_mkprop_fast(js, this_val, "name", 4, name); - set_slot(js, this_val, SLOT_ERROR_BRAND, js_true); + set_slot(this_val, SLOT_ERROR_BRAND, js_true); + js_capture_stack(js, this_val); return this_val; } @@ -4577,8 +4926,8 @@ static ant_value_t builtin_Error_toString(ant_t *js, ant_value_t *args, int narg ant_offset_t name_off = vstr(js, name, &name_len); ant_offset_t msg_off = vstr(js, msg, &msg_len); - const char *name_str = (const char *)&js->mem[name_off]; - const char *msg_str = (const char *)&js->mem[msg_off]; + const char *name_str = (const char *)(uintptr_t)(name_off); + const char *msg_str = (const char *)(uintptr_t)(msg_off); if (name_len == 0) return msg; if (msg_len == 0) return name; @@ -4605,8 +4954,8 @@ static ant_value_t builtin_AggregateError(ant_t *js, ant_value_t *args, int narg if (!is_new) { this_val = js_mkobj(js); ant_offset_t proto_off = lkp_interned(js, js_func_obj(js->current_func), INTERN_PROTOTYPE, 9); - if (proto_off) set_proto(js, this_val, resolveprop(js, mkval(T_PROP, proto_off))); - else set_proto(js, this_val, get_ctor_proto(js, "AggregateError", 14)); + if (proto_off) js_set_proto_init(this_val, propref_load(js, proto_off)); + else js_set_proto_init(this_val, get_ctor_proto(js, "AggregateError", 14)); } ant_value_t errors = nargs > 0 ? args[0] : mkarr(js); @@ -4624,11 +4973,11 @@ static ant_value_t builtin_AggregateError(ant_t *js, ant_value_t *args, int narg if (nargs > 2 && vtype(args[2]) == T_OBJ) { ant_offset_t cause_off = lkp(js, args[2], "cause", 5); - if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, resolveprop(js, mkval(T_PROP, cause_off))); + if (cause_off) js_mkprop_fast(js, this_val, "cause", 5, propref_load(js, cause_off)); } js_mkprop_fast(js, this_val, "name", 4, ANT_STRING("AggregateError")); - set_slot(js, this_val, SLOT_ERROR_BRAND, js_true); + set_slot(this_val, SLOT_ERROR_BRAND, js_true); return this_val; } @@ -4640,8 +4989,10 @@ typedef ant_value_t (*dynamic_kv_mapper_fn)( ant_value_t val ); -static ant_value_t iterate_dynamic_keys(ant_t *js, ant_value_t obj, dynamic_accessors_t *acc, dynamic_kv_mapper_fn mapper) { - ant_value_t keys_arr = acc->keys(js, obj); +static ant_value_t iterate_dynamic_keys(ant_t *js, ant_value_t obj, dynamic_kv_mapper_fn mapper) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->exotic_keys || !ptr->exotic_ops || !ptr->exotic_ops->getter) return mkarr(js); + ant_value_t keys_arr = ptr->exotic_keys(js, obj); ant_value_t arr = mkarr(js); ant_offset_t len = get_array_length(js, keys_arr); @@ -4649,8 +5000,8 @@ static ant_value_t iterate_dynamic_keys(ant_t *js, ant_value_t obj, dynamic_acce ant_value_t key_val = arr_get(js, keys_arr, i); if (vtype(key_val) != T_STR) continue; ant_offset_t klen; ant_offset_t str_off = vstr(js, key_val, &klen); - const char *key = (const char *)&js->mem[str_off]; - ant_value_t val = acc->getter(js, obj, key, klen); + const char *key = (const char *)(uintptr_t)(str_off); + ant_value_t val = ptr->exotic_ops->getter(js, obj, key, klen); js_arr_push(js, arr, mapper ? mapper(js, key_val, val) : val); } @@ -4702,15 +5053,14 @@ static ant_value_t map_to_entry(ant_t *js, ant_value_t key, ant_value_t val) { static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mode) { bool is_arr = (vtype(obj) == T_ARR); if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); - - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - dynamic_accessors_t *acc = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), acc); - if (acc && acc->keys) { - if (mode == OBJ_ENUM_KEYS && !acc->getter) return acc->keys(js, obj); - if (acc->getter) { + + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return mkarr(js); + if (ptr->is_exotic && ptr->exotic_keys) { + if (mode == OBJ_ENUM_KEYS && (!ptr->exotic_ops || !ptr->exotic_ops->getter)) return ptr->exotic_keys(js, obj); + if (ptr->exotic_ops && ptr->exotic_ops->getter) { dynamic_kv_mapper_fn mapper = (mode == OBJ_ENUM_ENTRIES) ? map_to_entry : NULL; - return iterate_dynamic_keys(js, obj, acc, mapper); + return iterate_dynamic_keys(js, obj, mapper); } } @@ -4718,11 +5068,11 @@ static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mo ant_offset_t idx = 0; if (is_arr) { - ant_offset_t doff = get_dense_buf_off(js, obj_off); + ant_offset_t doff = get_dense_buf(obj); if (doff) { - ant_offset_t dense_len = dense_length(js, doff); + ant_offset_t dense_len = dense_iterable_length(js, obj); for (ant_offset_t i = 0; i < dense_len; i++) { - ant_value_t v = dense_get(js, doff, i); + ant_value_t v = dense_get(doff, i); if (is_empty_slot(v)) continue; char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); ant_value_t key_val = js_mkstr(js, idxstr, idxlen); @@ -4733,41 +5083,39 @@ static ant_value_t object_enum(ant_t *js, ant_value_t obj, enum obj_enum_mode mo } } } - - ant_offset_t next = loadoff(js, obj_off) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - if (is_sym_key_prop(js, next)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - - next = next_prop(header); + + uint32_t shape_count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < shape_count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue; + if (i >= ptr->prop_count) continue; + + const char *key = prop->key.interned; + ant_offset_t klen = (ant_offset_t)strlen(key); + ant_value_t val = ant_object_prop_get_unchecked(ptr, i); + if (is_internal_prop(key, klen)) continue; if (is_arr && is_array_index(key, klen)) { - ant_offset_t doff = get_dense_buf_off(js, obj_off); + ant_offset_t doff = get_dense_buf(obj); if (doff) { unsigned long pidx = 0; for (ant_offset_t ci = 0; ci < klen; ci++) pidx = pidx * 10 + (key[ci] - '0'); - if (pidx < dense_length(js, doff)) continue; + if (pidx < dense_iterable_length(js, obj)) continue; } } - - bool should_include = true; - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, klen); - if (desc) should_include = desc->enumerable; - - if (should_include) { - ant_value_t key_val = js_mkstr(js, key, klen); - if (mode == OBJ_ENUM_KEYS) arr_set(js, arr, idx, key_val); - else if (mode == OBJ_ENUM_VALUES) arr_set(js, arr, idx, val); - else arr_set(js, arr, idx, map_to_entry(js, key_val, val)); - idx++; + + bool should_include = (ant_shape_get_attrs(ptr->shape, i) & ANT_PROP_ATTR_ENUMERABLE) != 0; + if (should_include && ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(js_as_obj(obj), key, (size_t)klen); + if (desc) should_include = desc->enumerable; } + if (!should_include) continue; + + ant_value_t key_val = js_mkstr(js, key, (size_t)klen); + if (mode == OBJ_ENUM_KEYS) arr_set(js, arr, idx, key_val); + else if (mode == OBJ_ENUM_VALUES) arr_set(js, arr, idx, val); + else arr_set(js, arr, idx, map_to_entry(js, key_val, val)); + idx++; } return mkval(T_ARR, vdata(arr)); @@ -4778,8 +5126,8 @@ static ant_value_t builtin_object_keys(ant_t *js, ant_value_t *args, int nargs) ant_value_t obj = args[0]; if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); - if (is_proxy(js, obj)) { - proxy_data_t *data = get_proxy_data(obj); + if (is_proxy(obj)) { + ant_proxy_state_t *data = get_proxy_data(obj); if (!data) return mkarr(js); if (data->revoked) return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot perform 'ownKeys' on a proxy that has been revoked"); @@ -4787,7 +5135,7 @@ static ant_value_t builtin_object_keys(ant_t *js, ant_value_t *args, int nargs) ant_offset_t trap_off = lkp(js, data->handler, "ownKeys", 7); if (!trap_off) return object_enum(js, data->target, OBJ_ENUM_KEYS); - ant_value_t trap = resolveprop(js, mkval(T_PROP, trap_off)); + ant_value_t trap = propref_load(js, trap_off); uint8_t ft = vtype(trap); if (ft != T_FUNC && ft != T_CFUNC) return object_enum(js, data->target, OBJ_ENUM_KEYS); @@ -4806,7 +5154,8 @@ static ant_value_t builtin_object_keys(ant_t *js, ant_value_t *args, int nargs) for (ant_offset_t j = 0; j < i; j++) { ant_value_t kj = arr_get(js, result, j); ant_offset_t kj_len; ant_offset_t kj_off = vstr(js, kj, &kj_len); - if (ki_len == kj_len && memcmp(&js->mem[ki_off], &js->mem[kj_off], ki_len) == 0) + if (ki_len == kj_len && + memcmp((const void *)(uintptr_t)ki_off, (const void *)(uintptr_t)kj_off, ki_len) == 0) return js_mkerr_typed(js, JS_ERR_TYPE, "ownKeys trap result must not contain duplicate entries"); } } @@ -4821,7 +5170,7 @@ static ant_value_t for_in_keys_add(ant_t *js, ant_value_t out, ant_value_t seen, ant_offset_t key_len = 0; ant_offset_t key_off = vstr(js, key, &key_len); - const char *key_ptr = (const char *)&js->mem[key_off]; + const char *key_ptr = (const char *)(uintptr_t)(key_off); if (is_internal_prop(key_ptr, key_len)) return js_mkundef(); @@ -4853,54 +5202,36 @@ ant_value_t js_for_in_keys(ant_t *js, ant_value_t obj) { if (t == T_NULL || t == T_UNDEF) return out; ant_value_t seen = mkobj(js, 0); - ant_handle_t h_out = js_root(js, out); - ant_handle_t h_seen = js_root(js, seen); ant_value_t result = js_mkundef(); if (t == T_STR) { - result = for_in_keys_add_string_indices(js, js_deref(js, h_out), js_deref(js, h_seen), obj); - goto done; + result = for_in_keys_add_string_indices(js, out, seen, obj); + if (is_err(result)) return result; + return out; } if (t == T_OBJ) { - ant_value_t prim = get_slot(js, obj, SLOT_PRIMITIVE); + ant_value_t prim = get_slot(obj, SLOT_PRIMITIVE); if (vtype(prim) == T_STR) { - result = for_in_keys_add_string_indices(js, js_deref(js, h_out), js_deref(js, h_seen), prim); - if (is_err(result)) goto done; + result = for_in_keys_add_string_indices(js, out, seen, prim); + if (is_err(result)) return result; } } if (t == T_OBJ || t == T_ARR || t == T_FUNC) { ant_value_t own_keys = object_enum(js, obj, OBJ_ENUM_KEYS); - if (is_err(own_keys)) { - result = own_keys; - goto done; - } + if (is_err(own_keys)) return own_keys; if (vtype(own_keys) == T_ARR) { - ant_handle_t h_own = js_root(js, own_keys); - ant_offset_t own_len = js_arr_len(js, js_deref(js, h_own)); + ant_offset_t own_len = js_arr_len(js, own_keys); for (ant_offset_t i = 0; i < own_len; i++) { - ant_value_t key = js_arr_get(js, js_deref(js, h_own), i); - result = for_in_keys_add(js, js_deref(js, h_out), js_deref(js, h_seen), key); - if (is_err(result)) { - js_unroot(js, h_own); - goto done; - } + ant_value_t key = js_arr_get(js, own_keys, i); + result = for_in_keys_add(js, out, seen, key); + if (is_err(result)) return result; } - js_unroot(js, h_own); } } -done: - if (is_err(result)) { - js_unroot(js, h_seen); - js_unroot(js, h_out); - return result; - } - ant_value_t ret = js_deref(js, h_out); - js_unroot(js, h_seen); - js_unroot(js, h_out); - return ret; + return out; } static ant_value_t builtin_object_values(ant_t *js, ant_value_t *args, int nargs) { @@ -4944,9 +5275,8 @@ static ant_value_t builtin_object_setPrototypeOf(ant_t *js, ant_value_t *args, i return js_mkerr(js, "Object.setPrototypeOf: prototype must be an object or null"); } - for (ant_value_t cur = proto; pt != T_NULL && vtype(cur) != T_NULL; cur = get_proto(js, cur)) { - if (vdata(js_as_obj(cur)) == vdata(js_as_obj(obj))) return js_mkerr(js, "Cyclic __proto__ value"); - } + if (pt != T_NULL && proto_chain_contains(js, proto, obj)) + return js_mkerr(js, "Cyclic __proto__ value"); set_proto(js, obj, proto); return obj; @@ -4988,9 +5318,8 @@ static ant_value_t builtin_proto_setter(ant_t *js, ant_value_t *args, int nargs) return js_mkundef(); } - for (ant_value_t cur = proto; pt != T_NULL && vtype(cur) == T_OBJ; cur = get_proto(js, cur)) { - if (vdata(cur) == vdata(this_val)) return js_mkundef(); - } + if (pt != T_NULL && proto_chain_contains(js, proto, this_val)) + return js_mkundef(); set_proto(js, this_val, proto); return js_mkundef(); @@ -5008,35 +5337,28 @@ static ant_value_t builtin_object_create(ant_t *js, ant_value_t *args, int nargs ant_value_t obj = js_mkobj(js); if (pt == T_NULL) { - set_proto(js, obj, js_mknull()); - } else set_proto(js, obj, proto); - + js_set_proto_init(obj, js_mknull()); + } else js_set_proto_init(obj, proto); + if (nargs >= 2 && vtype(args[1]) == T_OBJ) { ant_value_t props = args[1]; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(props)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - ant_value_t descriptor = resolveprop(js, mkval(T_PROP, next)); - + ant_iter_t iter = js_prop_iter_begin(js, props); + const char *key = NULL; + size_t klen = 0; + ant_value_t descriptor = js_mkundef(); + while (js_prop_iter_next(&iter, &key, &klen, &descriptor)) { if (vtype(descriptor) == T_OBJ) { ant_offset_t val_off = lkp(js, descriptor, "value", 5); if (val_off != 0) { - ant_value_t val = resolveprop(js, mkval(T_PROP, val_off)); + ant_value_t val = propref_load(js, val_off); ant_value_t key_str = js_mkstr(js, key, klen); js_setprop(js, obj, key_str, val); } } - - next = next_prop(header); } + js_prop_iter_end(&iter); } - + return obj; } @@ -5060,7 +5382,7 @@ static ant_value_t builtin_object_hasOwn(ant_t *js, ant_value_t *args, int nargs ant_value_t as_obj = js_as_obj(obj); ant_offset_t key_len, key_off = vstr(js, key, &key_len); - const char *key_str = (char *) &js->mem[key_off]; + const char *key_str = (char *)(uintptr_t)(key_off); ant_offset_t off = lkp(js, as_obj, key_str, key_len); return mkval(T_BOOL, off != 0 ? 1 : 0); @@ -5076,8 +5398,8 @@ static ant_value_t builtin_object_groupBy(ant_t *js, ant_value_t *args, int narg return js_mkerr_typed(js, JS_ERR_TYPE, "callback is not a function"); ant_value_t result = js_mkobj(js); - set_proto(js, result, js_mknull()); - + js_set_proto_init(result, js_mknull()); + ant_offset_t len = get_array_length(js, items); for (ant_offset_t i = 0; i < len; i++) { ant_value_t val = arr_get(js, items, i); @@ -5090,12 +5412,12 @@ static ant_value_t builtin_object_groupBy(ant_t *js, ant_value_t *args, int narg ant_offset_t klen; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (char *)&js->mem[koff]; + const char *kptr = (char *)(uintptr_t)(koff); ant_offset_t grp_off = lkp(js, result, kptr, klen); ant_value_t group; if (grp_off) { - group = resolveprop(js, mkval(T_PROP, grp_off)); + group = propref_load(js, grp_off); } else { group = mkarr(js); js_setprop(js, result, key_str, group); @@ -5142,26 +5464,27 @@ static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, i prop_len = (ant_offset_t)strlen(prop_str); } else { ant_offset_t prop_off = vstr(js, prop, &prop_len); - prop_str = (char *) &js->mem[prop_off]; + prop_str = (char *)(uintptr_t)(prop_off); if (streq(prop_str, prop_len, STR_PROTO, STR_PROTO_LEN)) { return js_mkerr(js, "Cannot define " STR_PROTO " property"); } } - bool has_value = false, has_get = false, has_set = false, has_writable = false; + bool has_value = false, has_get = false, has_set = false; + bool has_writable = false, has_enumerable = false, has_configurable = false; ant_value_t value = js_mkundef(); bool writable = false, enumerable = false, configurable = false; ant_offset_t value_off = lkp(js, descriptor, "value", 5); if (value_off != 0) { has_value = true; - value = resolveprop(js, mkval(T_PROP, value_off)); + value = propref_load(js, value_off); } ant_offset_t get_off = lkp_interned(js, descriptor, INTERN_GET, 3); if (get_off != 0) { has_get = true; - ant_value_t getter = resolveprop(js, mkval(T_PROP, get_off)); + ant_value_t getter = propref_load(js, get_off); if (vtype(getter) != T_FUNC && vtype(getter) != T_UNDEF) { return js_mkerr(js, "Getter must be a function"); } @@ -5170,61 +5493,116 @@ static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, i ant_offset_t set_off = lkp_interned(js, descriptor, INTERN_SET, 3); if (set_off != 0) { has_set = true; - ant_value_t setter = resolveprop(js, mkval(T_PROP, set_off)); + ant_value_t setter = propref_load(js, set_off); if (vtype(setter) != T_FUNC && vtype(setter) != T_UNDEF) { return js_mkerr(js, "Setter must be a function"); } } - if ((has_value || has_writable) && (has_get || has_set)) { - return js_mkerr(js, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); - } - ant_offset_t writable_off = lkp(js, descriptor, "writable", 8); if (writable_off != 0) { has_writable = true; - ant_value_t w_val = resolveprop(js, mkval(T_PROP, writable_off)); + ant_value_t w_val = propref_load(js, writable_off); writable = js_truthy(js, w_val); } ant_offset_t enumerable_off = lkp(js, descriptor, "enumerable", 10); if (enumerable_off != 0) { - ant_value_t e_val = resolveprop(js, mkval(T_PROP, enumerable_off)); + has_enumerable = true; + ant_value_t e_val = propref_load(js, enumerable_off); enumerable = js_truthy(js, e_val); } ant_offset_t configurable_off = lkp(js, descriptor, "configurable", 12); if (configurable_off != 0) { - ant_value_t c_val = resolveprop(js, mkval(T_PROP, configurable_off)); + has_configurable = true; + ant_value_t c_val = propref_load(js, configurable_off); configurable = js_truthy(js, c_val); } + + if ((has_value || has_writable) && (has_get || has_set)) { + return js_mkerr(js, "Invalid property descriptor. Cannot both specify accessors and a value or writable attribute"); + } + + ant_object_t *arr_ptr = (!sym_key && is_length_key(prop_str, prop_len)) + ? array_obj_ptr(as_obj) + : NULL; + if (arr_ptr) { + if (has_get || has_set) return js_mkerr(js, "Invalid property descriptor. Cannot use accessors for array length"); + if ((has_enumerable && enumerable) || (has_configurable && configurable)) + return js_mkerr(js, "Cannot redefine array length attributes"); + + ant_offset_t new_len = (ant_offset_t)arr_ptr->u.array.len; + if (has_value) { + ant_value_t len_err = validate_array_length(js, value); + if (is_err(len_err)) return len_err; + new_len = (ant_offset_t)tod(value); + } + + ant_offset_t doff = get_dense_buf(as_obj); + if (doff) { + ant_offset_t cap = dense_capacity(doff); + ant_offset_t cur_len = get_array_length(js, as_obj); + ant_offset_t clear_to = (cur_len < cap) ? cur_len : cap; + if (new_len < clear_to) { + for (ant_offset_t i = new_len; i < clear_to; i++) dense_set(js, doff, i, T_EMPTY); + } + } + + array_len_set(js, as_obj, new_len); + return obj; + } ant_offset_t existing_off = sym_key ? lkp_sym(js, as_obj, sym_off) : lkp(js, as_obj, prop_str, prop_len); + prop_meta_t existing_sym_meta; + bool has_existing_sym_meta = sym_key && lookup_symbol_prop_meta(as_obj, sym_off, &existing_sym_meta); + bool has_existing_prop = (existing_off > 0) || has_existing_sym_meta; + ant_object_t *obj_ptr = js_obj_ptr(as_obj); - if (existing_off == 0) { - if (js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))) + if (!has_existing_prop) { + if (obj_ptr && obj_ptr->frozen) return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); - if (js_truthy(js, get_slot(js, as_obj, SLOT_SEALED))) + if (obj_ptr && obj_ptr->sealed) return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); - if (get_slot(js, as_obj, SLOT_EXTENSIBLE) == js_false) + if (obj_ptr && !obj_ptr->extensible) return js_mkerr(js, "Cannot define property %.*s, object is not extensible", (int)prop_len, prop_str); } + if (has_existing_sym_meta) { + if (!has_writable) writable = existing_sym_meta.writable; + if (!has_enumerable) enumerable = existing_sym_meta.enumerable; + if (!has_configurable) configurable = existing_sym_meta.configurable; + } else if (existing_off > 0) { + ant_prop_ref_t *existing_ref = propref_get(js, existing_off); + if (existing_ref && existing_ref->obj && existing_ref->obj->shape) { + const ant_shape_prop_t *existing_prop = + ant_shape_prop_at(existing_ref->obj->shape, existing_ref->slot); + if (existing_prop) { + uint8_t existing_attrs = existing_prop->attrs; + if (!has_writable) writable = (existing_attrs & ANT_PROP_ATTR_WRITABLE) != 0; + if (!has_enumerable) enumerable = (existing_attrs & ANT_PROP_ATTR_ENUMERABLE) != 0; + if (!has_configurable) configurable = (existing_attrs & ANT_PROP_ATTR_CONFIGURABLE) != 0; + } + } + } + if (has_get || has_set) { int desc_flags = (enumerable ? JS_DESC_E : 0) | (configurable ? JS_DESC_C : 0); - if (!sym_key) { + ant_value_t getter = has_get ? propref_load(js, get_off) : js_mkundef(); + ant_value_t setter = has_set ? propref_load(js, set_off) : js_mkundef(); + + if (sym_key) { + if (has_get) js_set_sym_getter_desc(js, as_obj, prop, getter, desc_flags); + if (has_set) js_set_sym_setter_desc(js, as_obj, prop, setter, desc_flags); + } else { if (has_get && has_set) { - ant_value_t getter = resolveprop(js, mkval(T_PROP, get_off)); - ant_value_t setter = resolveprop(js, mkval(T_PROP, set_off)); js_set_accessor_desc(js, as_obj, prop_str, prop_len, getter, setter, desc_flags); } else if (has_get) { - ant_value_t getter = resolveprop(js, mkval(T_PROP, get_off)); js_set_getter_desc(js, as_obj, prop_str, prop_len, getter, desc_flags); } else { - ant_value_t setter = resolveprop(js, mkval(T_PROP, set_off)); js_set_setter_desc(js, as_obj, prop_str, prop_len, setter, desc_flags); } } @@ -5233,11 +5611,16 @@ static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, i (writable ? JS_DESC_W : 0) | (enumerable ? JS_DESC_E : 0) | (configurable ? JS_DESC_C : 0); - + + uint8_t attrs = 0; + if (writable) attrs |= ANT_PROP_ATTR_WRITABLE; + if (enumerable) attrs |= ANT_PROP_ATTR_ENUMERABLE; + if (configurable) attrs |= ANT_PROP_ATTR_CONFIGURABLE; + if (!sym_key) js_set_descriptor(js, as_obj, prop_str, prop_len, desc_flags); if (existing_off > 0) { - bool is_frozen = js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN)); + bool is_frozen = obj_ptr ? obj_ptr->frozen : false; bool is_nonconfig = is_nonconfig_prop(js, existing_off) || is_frozen; bool is_readonly = is_const_prop(js, existing_off) || is_frozen; @@ -5253,36 +5636,86 @@ static ant_value_t builtin_object_defineProperty(ant_t *js, ant_value_t *args, i ); } - if (is_readonly && has_value) return js_mkerr(js, "Cannot assign to read-only property '%.*s'", (int)prop_len, prop_str); - if (has_value) saveval(js, existing_off + sizeof(ant_offset_t) * 2, value); - - if (!writable || !configurable) { - ant_offset_t head = (ant_offset_t) vdata(as_obj); - ant_offset_t firstprop = loadoff(js, head); - if ((firstprop & ~(3U | FLAGMASK)) == existing_off) { - ant_offset_t flags = 0; - if (!writable) flags |= CONSTMASK; - if (!configurable) flags |= NONCONFIGMASK; - saveoff(js, head, firstprop | flags); - } else { - ant_offset_t prop_header = loadoff(js, existing_off); - ant_offset_t flags = 0; - if (!writable) flags |= CONSTMASK; - if (!configurable) flags |= NONCONFIGMASK; - saveoff(js, existing_off, prop_header | flags); - } + if (is_nonconfig && is_readonly && has_value) + return js_mkerr(js, "Cannot assign to read-only property '%.*s'", (int)prop_len, prop_str); + if (has_value) js_saveval(js, existing_off, value); + + ant_prop_ref_t *ref = propref_get(js, existing_off); + if (ref && ref->obj) { + if (!js_obj_ensure_unique_shape(ref->obj)) return js_mkerr(js, "oom"); + if (sym_key) ant_shape_set_attrs_symbol(ref->obj->shape, sym_off, attrs); + else ant_shape_set_attrs_interned(ref->obj->shape, intern_string(prop_str, prop_len), attrs); + ant_shape_clear_accessor_slot(ref->obj->shape, ref->slot); } } else { if (!has_value) value = js_mkundef(); ant_value_t prop_key = sym_key ? prop : js_mkstr(js, prop_str, prop_len); - ant_offset_t flags = (writable ? 0 : CONSTMASK) | (configurable ? 0 : NONCONFIGMASK); - mkprop(js, as_obj, prop_key, value, flags); + uint8_t prop_attrs = ANT_PROP_ATTR_ENUMERABLE + | (writable ? ANT_PROP_ATTR_WRITABLE : 0) + | (configurable ? ANT_PROP_ATTR_CONFIGURABLE : 0); + mkprop(js, as_obj, prop_key, value, prop_attrs); + + if (obj_ptr && obj_ptr->shape) { + if (!js_obj_ensure_unique_shape(obj_ptr)) return js_mkerr(js, "oom"); + if (sym_key) { + ant_shape_set_attrs_symbol(obj_ptr->shape, sym_off, attrs); + int32_t slot = ant_shape_lookup_symbol(obj_ptr->shape, sym_off); + if (slot >= 0) ant_shape_clear_accessor_slot(obj_ptr->shape, (uint32_t)slot); + } else { + const char *interned = intern_string(prop_str, prop_len); + if (interned) { + ant_shape_set_attrs_interned(obj_ptr->shape, interned, attrs); + int32_t slot = ant_shape_lookup_interned(obj_ptr->shape, interned); + if (slot >= 0) ant_shape_clear_accessor_slot(obj_ptr->shape, (uint32_t)slot); + } + } + } } } - + + if (!sym_key) array_define_or_set_index(js, as_obj, prop_str, (size_t)prop_len); + return obj; } +typedef struct { + bool thrown_exists; + ant_value_t thrown_value; + ant_value_t thrown_stack; +} js_exception_state_t; + +static inline js_exception_state_t js_save_exception(ant_t *js) { + js_exception_state_t saved = { + .thrown_exists = js->thrown_exists, + .thrown_value = js_mkundef(), + .thrown_stack = js_mkundef(), + }; + if (saved.thrown_exists) { + saved.thrown_value = js->thrown_value; + saved.thrown_stack = js->thrown_stack; + } + return saved; +} + +static inline void js_restore_exception(ant_t *js, const js_exception_state_t *saved) { + js->thrown_exists = saved->thrown_exists; + js->thrown_value = saved->thrown_value; + js->thrown_stack = saved->thrown_stack; +} + +ant_value_t js_define_property(ant_t *js, ant_value_t obj, ant_value_t prop, ant_value_t descriptor, bool reflect_mode) { + js_exception_state_t saved = js_save_exception(js); + ant_value_t args[3] = { obj, prop, descriptor }; + ant_value_t result = builtin_object_defineProperty(js, args, 3); + + if (!reflect_mode) return result; + if (is_err(result)) { + js_restore_exception(js, &saved); + return js_false; + } + return js_true; +} + static ant_value_t builtin_object_defineProperties(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "Object.defineProperties requires 2 arguments"); @@ -5298,26 +5731,22 @@ static ant_value_t builtin_object_defineProperties(ant_t *js, ant_value_t *args, return js_mkerr(js, "Property descriptors must be an object"); } - ant_value_t props_obj = props; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(props_obj)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - ant_value_t descriptor = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - - next = next_prop(header); - if (is_internal_prop(key, klen)) continue; - - ant_value_t prop_key = js_mkstr(js, key, klen); + ant_iter_t iter = js_prop_iter_begin(js, props); + const char *key = NULL; + size_t key_len = 0; + ant_value_t descriptor = js_mkundef(); + + while (js_prop_iter_next(&iter, &key, &key_len, &descriptor)) { + if (is_internal_prop(key, key_len)) continue; + ant_value_t prop_key = js_mkstr(js, key, key_len); ant_value_t define_args[3] = { obj, prop_key, descriptor }; ant_value_t result = builtin_object_defineProperty(js, define_args, 3); - if (is_err(result)) return result; + if (is_err(result)) { + js_prop_iter_end(&iter); + return result; + } } + js_prop_iter_end(&iter); return obj; } @@ -5345,32 +5774,35 @@ static ant_value_t builtin_object_assign(ant_t *js, ant_value_t *args, int nargs if (st == T_NULL || st == T_UNDEF) continue; if (st != T_OBJ && st != T_ARR && st != T_FUNC) continue; - ant_value_t src_obj = js_as_obj(source); - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(src_obj)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - - next = next_prop(header); - if (is_internal_prop(key, klen)) continue; - + ant_iter_t iter = js_prop_iter_begin(js, source); + const char *key = NULL; + size_t key_len = 0; + ant_value_t val = js_mkundef(); + + while (js_prop_iter_next(&iter, &key, &key_len, &val)) { + if (is_internal_prop(key, key_len)) continue; + + ant_object_t *source_ptr = js_obj_ptr(js_as_obj(source)); bool should_copy = true; - ant_offset_t src_obj_off = (ant_offset_t)vdata(src_obj); - - descriptor_entry_t *desc = lookup_descriptor(js, src_obj_off, key, klen); - if (desc) should_copy = desc->enumerable; - + if (source_ptr && !source_ptr->is_exotic) { + const char *interned = intern_string(key, key_len); + if (interned && source_ptr->shape) { + int32_t slot = ant_shape_lookup_interned(source_ptr->shape, interned); + if (slot >= 0) { + should_copy = (ant_shape_get_attrs(source_ptr->shape, (uint32_t)slot) & ANT_PROP_ATTR_ENUMERABLE) != 0; + } + } + } else { + descriptor_entry_t *desc = lookup_descriptor(js_as_obj(source), key, key_len); + if (desc) should_copy = desc->enumerable; + } + if (should_copy) { - ant_value_t key_str = js_mkstr(js, key, klen); + ant_value_t key_str = js_mkstr(js, key, key_len); js_setprop(js, as_obj, key_str, val); } } + js_prop_iter_end(&iter); } return target; @@ -5384,35 +5816,31 @@ static ant_value_t builtin_object_freeze(ant_t *js, ant_value_t *args, int nargs if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; ant_value_t as_obj = js_as_obj(obj); - - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(as_obj)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - - ant_offset_t cur_prop = next; - next = next_prop(header); - if (is_internal_prop(key, klen)) continue; - - ant_offset_t freeze_flags = CONSTMASK | NONCONFIGMASK; - ant_offset_t head = (ant_offset_t) vdata(as_obj); - ant_offset_t firstprop = loadoff(js, head); - if ((firstprop & ~(3U | FLAGMASK)) == cur_prop) { - saveoff(js, head, firstprop | freeze_flags); + + ant_object_t *ptr = js_obj_ptr(as_obj); + if (!ptr || !ptr->shape) return obj; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop) continue; + + uint8_t attrs = ant_shape_get_attrs(ptr->shape, i); + attrs &= (uint8_t)~ANT_PROP_ATTR_WRITABLE; + attrs &= (uint8_t)~ANT_PROP_ATTR_CONFIGURABLE; + + if (prop->type == ANT_SHAPE_KEY_STRING) { + const char *key = prop->key.interned; + size_t klen = strlen(key); + if (is_internal_prop(key, klen)) continue; + ant_shape_set_attrs_interned(ptr->shape, key, attrs); } else { - ant_offset_t prop_header = loadoff(js, cur_prop); - saveoff(js, cur_prop, prop_header | freeze_flags); + ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs); } - - js_set_descriptor(js, as_obj, key, klen, JS_DESC_E); } - set_slot(js, as_obj, SLOT_FROZEN, js_true); + ptr->frozen = 1; return obj; } @@ -5425,7 +5853,8 @@ static ant_value_t builtin_object_isFrozen(ant_t *js, ant_value_t *args, int nar if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true; ant_value_t as_obj = js_as_obj(obj); - return js_bool(js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))); + ant_object_t *ptr = js_obj_ptr(as_obj); + return js_bool(ptr && ptr->frozen); } static ant_value_t builtin_object_seal(ant_t *js, ant_value_t *args, int nargs) { @@ -5437,32 +5866,27 @@ static ant_value_t builtin_object_seal(ant_t *js, ant_value_t *args, int nargs) if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; ant_value_t as_obj = js_as_obj(obj); - set_slot(js, as_obj, SLOT_SEALED, js_true); - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(as_obj)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *) &js->mem[koff + sizeof(koff)]; - - ant_offset_t cur_prop = next; - next = next_prop(header); - - if (is_internal_prop(key, klen)) continue; - - ant_offset_t head = (ant_offset_t) vdata(as_obj); - ant_offset_t firstprop = loadoff(js, head); - if ((firstprop & ~(3U | FLAGMASK)) == cur_prop) { - saveoff(js, head, firstprop | NONCONFIGMASK); + ant_object_t *ptr = js_obj_ptr(as_obj); + if (!ptr || !ptr->shape) return obj; + if (!js_obj_ensure_unique_shape(ptr)) return js_mkerr(js, "oom"); + ptr->sealed = 1; + + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop) continue; + + uint8_t attrs = ant_shape_get_attrs(ptr->shape, i); + attrs &= (uint8_t)~ANT_PROP_ATTR_CONFIGURABLE; + + if (prop->type == ANT_SHAPE_KEY_STRING) { + const char *key = prop->key.interned; + size_t klen = strlen(key); + if (is_internal_prop(key, klen)) continue; + ant_shape_set_attrs_interned(ptr->shape, key, attrs); } else { - ant_offset_t prop_header = loadoff(js, cur_prop); - saveoff(js, cur_prop, prop_header | NONCONFIGMASK); + ant_shape_set_attrs_symbol(ptr->shape, prop->key.sym_off, attrs); } - - js_set_descriptor(js, as_obj, key, klen, JS_DESC_W | JS_DESC_E); } return obj; @@ -5477,8 +5901,8 @@ static ant_value_t builtin_object_isSealed(ant_t *js, ant_value_t *args, int nar if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true; ant_value_t as_obj = js_as_obj(obj); - if (js_truthy(js, get_slot(js, as_obj, SLOT_SEALED))) return js_true; - if (js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))) return js_true; + ant_object_t *ptr = js_obj_ptr(as_obj); + if (ptr && (ptr->sealed || ptr->frozen)) return js_true; return js_false; } @@ -5536,61 +5960,92 @@ static ant_value_t builtin_object_getOwnPropertyDescriptor(ant_t *js, ant_value_ key_len = (ant_offset_t)strlen(key_str); } else if (vtype(key) == T_STR) { ant_offset_t key_off = vstr(js, key, &key_len); - key_str = (char *) &js->mem[key_off]; + key_str = (char *)(uintptr_t)(key_off); } else { char buf[64]; size_t n = tostr(js, key, buf, sizeof(buf)); key = js_mkstr(js, buf, n); ant_offset_t key_off = vstr(js, key, &key_len); - key_str = (char *) &js->mem[key_off]; + key_str = (char *)(uintptr_t)(key_off); } ant_value_t as_obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(as_obj); - - descriptor_entry_t *desc = is_sym - ? lookup_sym_descriptor(obj_off, (ant_offset_t)vdata(key)) - : lookup_descriptor(js, obj_off, key_str, key_len); - - ant_offset_t prop_off = is_sym ? lkp_sym(js, as_obj, (ant_offset_t)vdata(key)) : lkp(js, as_obj, key_str, key_len); - if (prop_off == 0 && !desc) { + + ant_offset_t sym_off = is_sym ? (ant_offset_t)vdata(key) : 0; + prop_meta_t sym_meta; + prop_meta_t str_meta; + bool has_sym_meta = is_sym ? lookup_symbol_prop_meta(as_obj, sym_off, &sym_meta) : false; + bool has_str_meta = is_sym ? false : lookup_string_prop_meta(js, as_obj, key_str, (size_t)key_len, &str_meta); + + ant_offset_t prop_off = is_sym ? lkp_sym(js, as_obj, sym_off) : lkp(js, as_obj, key_str, key_len); + if (prop_off == 0 && !(is_sym ? has_sym_meta : has_str_meta)) { return js_mkundef(); } ant_value_t result = js_mkobj(js); - - if (desc && (desc->has_getter || desc->has_setter)) { - if (desc->has_getter) { - js_setprop(js, result, js_mkstr(js, "get", 3), desc->getter); - } - if (desc->has_setter) { - js_setprop(js, result, js_mkstr(js, "set", 3), desc->setter); - } - js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(desc->enumerable)); - js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(desc->configurable)); + + bool has_getter = false; + bool has_setter = false; + ant_value_t getter = js_mkundef(); + ant_value_t setter = js_mkundef(); + bool writable = true; + bool enumerable = true; + bool configurable = true; + + if (is_sym && has_sym_meta) { + has_getter = sym_meta.has_getter; + has_setter = sym_meta.has_setter; + getter = sym_meta.getter; + setter = sym_meta.setter; + writable = sym_meta.writable; + enumerable = sym_meta.enumerable; + configurable = sym_meta.configurable; + } else if (!is_sym && has_str_meta) { + has_getter = str_meta.has_getter; + has_setter = str_meta.has_setter; + getter = str_meta.getter; + setter = str_meta.setter; + writable = str_meta.writable; + enumerable = str_meta.enumerable; + configurable = str_meta.configurable; + } + + if (has_getter || has_setter) { + if (has_getter) { + js_setprop(js, result, js_mkstr(js, "get", 3), getter); + } + if (has_setter) { + js_setprop(js, result, js_mkstr(js, "set", 3), setter); + } + js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(enumerable)); + js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(configurable)); } else { + ant_value_t prop_val = js_mkundef(); + bool has_value_out = false; if (prop_off != 0) { - ant_value_t prop_val = resolveprop(js, mkval(T_PROP, prop_off)); - js_setprop(js, result, js_mkstr(js, "value", 5), prop_val); + prop_val = propref_load(js, prop_off); + has_value_out = true; + } else if (!is_sym && is_length_key(key_str, key_len) && array_obj_ptr(as_obj)) { + prop_val = tov((double)get_array_length(js, as_obj)); + has_value_out = true; } - js_setprop(js, result, js_mkstr(js, "writable", 8), desc ? (js_bool(desc->writable)) : js_true); - js_setprop(js, result, js_mkstr(js, "enumerable", 10), desc ? (js_bool(desc->enumerable)) : js_true); - js_setprop(js, result, js_mkstr(js, "configurable", 12), desc ? (js_bool(desc->configurable)) : js_true); + if (has_value_out) js_setprop(js, result, js_mkstr(js, "value", 5), prop_val); + js_setprop(js, result, js_mkstr(js, "writable", 8), js_bool(writable)); + js_setprop(js, result, js_mkstr(js, "enumerable", 10), js_bool(enumerable)); + js_setprop(js, result, js_mkstr(js, "configurable", 12), js_bool(configurable)); } return result; - - return js_mkundef(); } static inline bool own_prop_names_is_dense_shadow( - ant_t *js, ant_offset_t obj_off, + ant_t *js, ant_value_t obj, const char *key, ant_offset_t key_len ) { - ant_offset_t doff = get_dense_buf_off(js, obj_off); + ant_offset_t doff = get_dense_buf(obj); if (!doff) return false; - ant_offset_t dense_len = dense_length(js, doff); + ant_offset_t dense_len = dense_iterable_length(js, obj); if (dense_len <= 0 || !is_array_index(key, key_len)) return false; unsigned long dense_idx = 0; @@ -5604,7 +6059,8 @@ static ant_value_t builtin_object_getOwnPropertyNames(ant_t *js, ant_value_t *ar if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR && vtype(obj) != T_FUNC) return mkarr(js); if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return mkarr(js); bool is_arr_obj = (vtype(obj) == T_ARR); ant_value_t arr = mkarr(js); @@ -5612,33 +6068,29 @@ static ant_value_t builtin_object_getOwnPropertyNames(ant_t *js, ant_value_t *ar if (is_arr_obj) { for (ant_offset_t i = 0;; i++) { - ant_offset_t doff = get_dense_buf_off(js, obj_off); + ant_offset_t doff = get_dense_buf(obj); if (!doff) break; - ant_offset_t dense_len = dense_length(js, doff); + ant_offset_t dense_len = dense_iterable_length(js, obj); if (i >= dense_len) break; - ant_value_t v = dense_get(js, doff, i); + ant_value_t v = dense_get(doff, i); if (is_empty_slot(v)) continue; char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)i); arr_set(js, arr, idx++, js_mkstr(js, idxstr, idxlen)); }} - ant_offset_t next = loadoff(js, obj_off) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t prop = next; - ant_offset_t header = loadoff(js, prop); - next = next_prop(header); - if (is_slot_prop(header) || is_sym_key_prop(js, prop)) continue; - - ant_offset_t koff = loadoff(js, prop + (ant_offset_t)sizeof(prop)); - ant_offset_t klen = offtolen(loadoff(js, koff)); - const char *key = (char *)&js->mem[koff + sizeof(koff)]; + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop || prop->type == ANT_SHAPE_KEY_SYMBOL) continue; + const char *key = prop->key.interned; + ant_offset_t klen = (ant_offset_t)strlen(key); if (is_internal_prop(key, klen)) continue; - if (is_arr_obj && own_prop_names_is_dense_shadow(js, obj_off, key, klen)) continue; - arr_set(js, arr, idx++, js_mkstr(js, key, klen)); + if (is_arr_obj && own_prop_names_is_dense_shadow(js, obj, key, klen)) continue; + arr_set(js, arr, idx++, js_mkstr(js, key, (size_t)klen)); } if (is_arr_obj) arr_set(js, arr, idx++, js->length_str); @@ -5652,18 +6104,19 @@ static ant_value_t builtin_object_getOwnPropertySymbols(ant_t *js, ant_value_t * uint8_t t = vtype(obj); if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkarr(js); if (t == T_FUNC) obj = js_func_obj(obj); - - ant_value_t arr = mkarr(js); ant_offset_t idx = 0; - ant_offset_t next = loadoff(js, (ant_offset_t)vdata(obj)) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (!is_slot_prop(header) && is_sym_key_prop(js, next)) { - arr_set(js, arr, idx++, mkval(T_SYMBOL, get_prop_koff(js, next))); - } - next = next_prop(header); + + ant_object_t *ptr = js_obj_ptr(obj); + ant_value_t arr = mkarr(js); + ant_offset_t idx = 0; + if (!ptr || !ptr->shape) return mkval(T_ARR, vdata(arr)); + + uint32_t count = ant_shape_count(ptr->shape); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, i); + if (!prop || prop->type != ANT_SHAPE_KEY_SYMBOL) continue; + arr_set(js, arr, idx++, mkval(T_SYMBOL, prop->key.sym_off)); } - + return mkval(T_ARR, vdata(arr)); } @@ -5676,13 +6129,10 @@ static ant_value_t builtin_object_isExtensible(ant_t *js, ant_value_t *args, int if (t != T_OBJ && t != T_ARR && t != T_FUNC) return js_true; ant_value_t as_obj = js_as_obj(obj); - if (js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))) return js_false; - if (js_truthy(js, get_slot(js, as_obj, SLOT_SEALED))) return js_false; - - ant_value_t ext_slot = get_slot(js, as_obj, SLOT_EXTENSIBLE); - if (vtype(ext_slot) != T_UNDEF) return js_bool(js_truthy(js, ext_slot)); - - return js_true; + ant_object_t *ptr = js_obj_ptr(as_obj); + if (!ptr) return js_true; + if (ptr->frozen || ptr->sealed) return js_false; + return js_bool(ptr->extensible); } static ant_value_t builtin_object_preventExtensions(ant_t *js, ant_value_t *args, int nargs) { @@ -5694,7 +6144,8 @@ static ant_value_t builtin_object_preventExtensions(ant_t *js, ant_value_t *args if (t != T_OBJ && t != T_ARR && t != T_FUNC) return obj; ant_value_t as_obj = js_as_obj(obj); - set_slot(js, as_obj, SLOT_EXTENSIBLE, js_false); + ant_object_t *ptr = js_obj_ptr(as_obj); + if (ptr) ptr->extensible = 0; return obj; } @@ -5704,15 +6155,18 @@ static ant_value_t builtin_object_hasOwnProperty(ant_t *js, ant_value_t *args, i ant_value_t obj = js->this_val; ant_value_t key = args[0]; - obj = resolveprop(js, obj); uint8_t t = vtype(obj); if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); ant_value_t as_obj = js_as_obj(obj); + bool is_arr_obj = array_obj_ptr(as_obj) != NULL; if (vtype(key) == T_SYMBOL) { - ant_offset_t off = lkp_sym(js, as_obj, (ant_offset_t)vdata(key)); - return mkval(T_BOOL, off != 0 ? 1 : 0); + ant_offset_t sym_off = (ant_offset_t)vdata(key); + ant_offset_t off = lkp_sym(js, as_obj, sym_off); + if (off != 0) return mkval(T_BOOL, 1); + prop_meta_t meta; + return mkval(T_BOOL, lookup_symbol_prop_meta(as_obj, sym_off, &meta) ? 1 : 0); } const char *key_str = NULL; @@ -5723,44 +6177,40 @@ static ant_value_t builtin_object_hasOwnProperty(ant_t *js, ant_value_t *args, i key = js_mkstr(js, buf, n); } ant_offset_t key_off = vstr(js, key, &key_len); - key_str = (char *) &js->mem[key_off]; + key_str = (char *)(uintptr_t)(key_off); - if (t == T_ARR && streq(key_str, key_len, "length", 6)) return mkval(T_BOOL, 1); - if (t == T_ARR && is_array_index(key_str, key_len)) { + if (is_arr_obj && is_length_key(key_str, key_len)) return mkval(T_BOOL, 1); + if (is_arr_obj && is_array_index(key_str, key_len)) { unsigned long idx; - if (parse_array_index(key_str, key_len, get_array_length(js, obj), &idx)) { - return mkval(T_BOOL, arr_has(js, obj, (ant_offset_t)idx) ? 1 : 0); + if (parse_array_index(key_str, key_len, get_array_length(js, as_obj), &idx)) { + return mkval(T_BOOL, arr_has(js, as_obj, (ant_offset_t)idx) ? 1 : 0); } } ant_offset_t off = lkp(js, as_obj, key_str, key_len); if (off != 0) return mkval(T_BOOL, 1); - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(as_obj), key_str, key_len); - return mkval(T_BOOL, (desc && (desc->has_getter || desc->has_setter)) ? 1 : 0); + ant_object_t *ptr = js_obj_ptr(as_obj); + if (ptr && ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(as_obj, key_str, key_len); + return mkval(T_BOOL, (desc && (desc->has_getter || desc->has_setter)) ? 1 : 0); + } + return mkval(T_BOOL, 0); } -static ant_value_t builtin_object_isPrototypeOf(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 1) return mkval(T_BOOL, 0); - - ant_value_t proto_obj = resolveprop(js, js->this_val); - ant_value_t obj = args[0]; - +static bool proto_chain_contains_cycle_safe(ant_t *js, ant_value_t start, ant_value_t target); + +bool js_is_prototype_of(ant_t *js, ant_value_t proto_obj, ant_value_t obj) { uint8_t obj_type = vtype(obj); - if (obj_type != T_OBJ && obj_type != T_ARR && obj_type != T_FUNC) return mkval(T_BOOL, 0); - + if (obj_type != T_OBJ && obj_type != T_ARR && obj_type != T_FUNC) return false; uint8_t proto_type = vtype(proto_obj); - if (proto_type != T_OBJ && proto_type != T_ARR && proto_type != T_FUNC) return mkval(T_BOOL, 0); - ant_offset_t proto_data = (ant_offset_t)vdata(js_as_obj(proto_obj)); - + if (proto_type != T_OBJ && proto_type != T_ARR && proto_type != T_FUNC) return false; ant_value_t current = get_proto(js, obj); - while (!is_undefined(current) && !is_null(current)) { - uint8_t cur_type = vtype(current); - if (cur_type != T_OBJ && cur_type != T_ARR && cur_type != T_FUNC) break; - if ((ant_offset_t)vdata(js_as_obj(current)) == proto_data) return mkval(T_BOOL, 1); - current = get_proto(js, current); - } - - return mkval(T_BOOL, 0); + return proto_chain_contains_cycle_safe(js, current, proto_obj); +} + +ant_value_t builtin_object_isPrototypeOf(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return mkval(T_BOOL, 0); + return mkval(T_BOOL, js_is_prototype_of(js, js->this_val, args[0]) ? 1 : 0); } static ant_value_t builtin_object_propertyIsEnumerable(ant_t *js, ant_value_t *args, int nargs) { @@ -5769,15 +6219,20 @@ static ant_value_t builtin_object_propertyIsEnumerable(ant_t *js, ant_value_t *a ant_value_t obj = js->this_val; ant_value_t key = args[0]; - obj = resolveprop(js, obj); uint8_t t = vtype(obj); if (t != T_OBJ && t != T_ARR && t != T_FUNC) return mkval(T_BOOL, 0); ant_value_t as_obj = js_as_obj(obj); + bool is_arr_obj = array_obj_ptr(as_obj) != NULL; if (vtype(key) == T_SYMBOL) { - ant_offset_t off = lkp_sym(js, as_obj, (ant_offset_t)vdata(key)); - return mkval(T_BOOL, off != 0 ? 1 : 0); + ant_offset_t sym_off = (ant_offset_t)vdata(key); + ant_offset_t off = lkp_sym(js, as_obj, sym_off); + if (off == 0) return mkval(T_BOOL, 0); + prop_meta_t meta; + if (lookup_symbol_prop_meta(as_obj, sym_off, &meta)) + return mkval(T_BOOL, meta.enumerable ? 1 : 0); + return mkval(T_BOOL, 1); } const char *key_str = NULL; @@ -5788,31 +6243,34 @@ static ant_value_t builtin_object_propertyIsEnumerable(ant_t *js, ant_value_t *a key = js_mkstr(js, buf, n); } ant_offset_t key_off = vstr(js, key, &key_len); - key_str = (char *) &js->mem[key_off]; + key_str = (char *)(uintptr_t)(key_off); - if (t == T_ARR && streq(key_str, key_len, "length", 6)) { + if (is_arr_obj && is_length_key(key_str, key_len)) { return mkval(T_BOOL, 0); } - if (t == T_ARR) { - ant_offset_t doff = get_dense_buf(js, obj); - if (doff) { - unsigned long idx; - if (parse_array_index(key_str, key_len, dense_length(js, doff), &idx)) { - return mkval(T_BOOL, !is_empty_slot(dense_get(js, doff, (ant_offset_t)idx)) ? 1 : 0); - } + if (is_arr_obj) { + unsigned long idx; + if (parse_array_index(key_str, key_len, get_array_length(js, as_obj), &idx)) { + return mkval(T_BOOL, arr_has(js, as_obj, (ant_offset_t)idx) ? 1 : 0); } } ant_offset_t off = lkp(js, as_obj, key_str, key_len); if (off == 0) return mkval(T_BOOL, 0); - - ant_offset_t obj_off = (ant_offset_t)vdata(as_obj); - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key_str, key_len); - if (desc) { - return mkval(T_BOOL, desc->enumerable ? 1 : 0); + + const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off); + if (prop_meta && !js_obj_ptr(as_obj)->is_exotic) { + bool enumerable = (prop_meta->attrs & ANT_PROP_ATTR_ENUMERABLE) != 0; + return mkval(T_BOOL, enumerable ? 1 : 0); + } + + ant_object_t *ptr = js_obj_ptr(as_obj); + if (ptr && ptr->is_exotic) { + prop_meta_t meta; + if (lookup_string_prop_meta(js, as_obj, key_str, (size_t)key_len, &meta)) + return mkval(T_BOOL, meta.enumerable ? 1 : 0); } - return mkval(T_BOOL, 1); } @@ -5820,7 +6278,6 @@ static ant_value_t builtin_object_toString(ant_t *js, ant_value_t *args, int nar (void)args; (void)nargs; ant_value_t obj = js->this_val; - obj = resolveprop(js, obj); uint8_t t = vtype(obj); const char *tag = NULL; @@ -5839,16 +6296,16 @@ static ant_value_t builtin_object_toString(ant_t *js, ant_value_t *args, int nar } } if (tag_off != 0) { - ant_value_t tag_val = resolveprop(js, mkval(T_PROP, tag_off)); + ant_value_t tag_val = propref_load(js, tag_off); if (vtype(tag_val) == T_STR) { ant_offset_t str_off = vstr(js, tag_val, &tag_len); - tag = (const char *)&js->mem[str_off]; + tag = (const char *)(uintptr_t)(str_off); } } } if (!tag) { - if (is_object_type(obj) && get_slot(js, obj, SLOT_ERROR_BRAND) == js_true) { + if (is_object_type(obj) && get_slot(obj, SLOT_ERROR_BRAND) == js_true) { tag = "Error"; tag_len = 5; } else switch (t) { case T_UNDEF: tag = "Undefined"; tag_len = 9; break; @@ -5901,10 +6358,10 @@ static ant_value_t array_shallow_copy(ant_t *js, ant_value_t arr, ant_offset_t l ant_value_t result = mkarr(js); if (is_err(result)) return result; - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { for (ant_offset_t i = 0; i < len; i++) { - ant_value_t v = dense_get(js, doff, i); + ant_value_t v = dense_get(doff, i); arr_set(js, result, i, v); } return result; @@ -5921,23 +6378,21 @@ static ant_value_t array_shallow_copy(ant_t *js, ant_value_t arr, ant_offset_t l } js_prop_iter_end(&iter); - js_mkprop_fast(js, result, "length", 6, tov((double)len)); + array_len_set(js, result, len); return result; } static ant_value_t builtin_array_push(ant_t *js, ant_value_t *args, int nargs) { ant_value_t arr = js->this_val; - arr = resolveprop(js, arr); - if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) { return js_mkerr(js, "push called on non-array"); } - if (is_proxy(js, arr)) { + if (is_proxy(arr)) { ant_offset_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); ant_offset_t len = 0; if (off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, off)); + ant_value_t len_val = propref_load(js, off); if (vtype(len_val) == T_NUM) len = (ant_offset_t) tod(len_val); } for (int i = 0; i < nargs; i++) { @@ -5955,23 +6410,21 @@ static ant_value_t builtin_array_push(ant_t *js, ant_value_t *args, int nargs) { ant_offset_t len = get_array_length(js, arr); - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { for (int i = 0; i < nargs; i++) { - ant_offset_t cap = dense_capacity(js, doff); + ant_offset_t cap = dense_capacity(doff); if (len >= cap) { doff = dense_grow(js, arr, len + 1); if (doff == 0) return js_mkerr(js, "oom"); } dense_set(js, doff, len, args[i]); len++; - dense_set_length(js, doff, len); } + array_len_set(js, arr, len); return tov((double) len); } - ant_offset_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); - for (int i = 0; i < nargs; i++) { char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); @@ -5979,42 +6432,33 @@ static ant_value_t builtin_array_push(ant_t *js, ant_value_t *args, int nargs) { } ant_value_t new_len = tov((double) len); - if (off != 0) saveval(js, off + sizeof(ant_offset_t) * 2, new_len); - else js_mkprop_fast(js, arr, "length", 6, new_len); + array_len_set(js, arr, len); return new_len; } void js_arr_push(ant_t *js, ant_value_t arr, ant_value_t val) { - arr = resolveprop(js, arr); if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) return; - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); - ant_offset_t cap = dense_capacity(js, doff); + ant_offset_t len = get_array_length(js, arr); + ant_offset_t cap = dense_capacity(doff); if (len >= cap) { doff = dense_grow(js, arr, len + 1); if (doff == 0) return; } dense_set(js, doff, len, val); - dense_set_length(js, doff, len + 1); + array_len_set(js, arr, len + 1); return; } - ant_offset_t len_off = lkp_interned(js, arr, INTERN_LENGTH, 6); - ant_offset_t len = 0; - if (len_off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, len_off)); - if (vtype(len_val) == T_NUM) len = (ant_offset_t) tod(len_val); - } + ant_offset_t len = get_array_length(js, arr); char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); js_mkprop_fast(js, arr, idxstr, idxlen, val); - - if (len_off != 0) saveval(js, len_off + sizeof(ant_offset_t) * 2, tov((double)(len + 1))); - else js_mkprop_fast(js, arr, "length", 6, tov((double)(len + 1))); + array_len_set(js, arr, len + 1); } static ant_value_t builtin_array_pop(ant_t *js, ant_value_t *args, int nargs) { @@ -6024,7 +6468,7 @@ static ant_value_t builtin_array_pop(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr(js, "pop called on non-array"); } - if (is_proxy(js, arr)) { + if (is_proxy(arr)) { ant_offset_t len = proxy_aware_length(js, arr); if (len == 0) { js_setprop(js, arr, js->length_str, tov(0.0)); @@ -6035,57 +6479,40 @@ static ant_value_t builtin_array_pop(ant_t *js, ant_value_t *args, int nargs) { size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); ant_value_t result = proxy_aware_get_elem(js, arr, idxstr, idxlen); js_setprop(js, arr, js->length_str, tov((double) len)); - js->needs_gc = true; return result; } - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { - ant_offset_t len = dense_length(js, doff); + ant_offset_t len = get_array_length(js, arr); + ant_offset_t dense_len = dense_iterable_length(js, arr); if (len == 0) return js_mkundef(); + if (len != dense_len) goto pop_slow; len--; - ant_value_t result = dense_get(js, doff, len); + ant_value_t result = (len < dense_len) ? dense_get(doff, len) : js_mkundef(); if (is_empty_slot(result)) result = js_mkundef(); - dense_set(js, doff, len, T_EMPTY); - dense_set_length(js, doff, len); + if (len < dense_len) { + dense_set(js, doff, len, T_EMPTY); + } + array_len_set(js, arr, len); return result; } - ant_offset_t off = lkp_interned(js, arr, INTERN_LENGTH, 6); - ant_offset_t len = 0; - - if (off != 0) { - ant_value_t len_val = resolveprop(js, mkval(T_PROP, off)); - if (vtype(len_val) == T_NUM) len = (ant_offset_t) tod(len_val); - } + pop_slow: + ant_offset_t len = get_array_length(js, arr); if (len == 0) return js_mkundef(); len--; char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)len); - ant_offset_t arr_off = (ant_offset_t)vdata(js_as_obj(arr)); - ant_offset_t tail = loadoff(js, arr_off + sizeof(ant_offset_t) * 2); ant_offset_t elem_off = 0; - if (tail != 0 && tail < js->brk) { - ant_offset_t tail_hdr = loadoff(js, tail); - if ((tail_hdr & SLOTMASK) == 0) { - ant_offset_t tail_koff = loadoff(js, tail + sizeof(ant_offset_t)); - ant_offset_t tail_klen = offtolen(loadoff(js, tail_koff)); - const char *tail_key = (char *)&js->mem[tail_koff + sizeof(ant_offset_t)]; - if (tail_klen == idxlen && memcmp(tail_key, idxstr, idxlen) == 0) elem_off = tail; - } - } - if (elem_off == 0) elem_off = lkp(js, arr, idxstr, idxlen); ant_value_t result = js_mkundef(); - if (elem_off != 0) result = resolveprop(js, mkval(T_PROP, elem_off)); + if (elem_off != 0) result = propref_load(js, elem_off); - if (off != 0) { - saveval(js, off + sizeof(ant_offset_t) * 2, tov((double) len)); - } else js_setprop(js, arr, js->length_str, tov((double) len)); + array_len_set(js, arr, len); - js->needs_gc = true; return result; } @@ -6141,7 +6568,7 @@ static ant_value_t builtin_array_join(ant_t *js, ant_value_t *args, int nargs) { if (vtype(args[0]) == T_STR) { sep_len = 0; ant_offset_t sep_off = vstr(js, args[0], &sep_len); - sep = (const char *) &js->mem[sep_off]; + sep = (const char *)(uintptr_t)(sep_off); } else if (vtype(args[0]) != T_UNDEF) { const char *sep_str = js_str(js, args[0]); sep = sep_str; @@ -6183,7 +6610,7 @@ static ant_value_t builtin_array_join(ant_t *js, ant_value_t *args, int nargs) { if (et == T_STR) { ant_offset_t soff, slen; soff = vstr(js, elem, &slen); - elem_str = (const char *)&js->mem[soff]; + elem_str = (const char *)(uintptr_t)(soff); elem_len = slen; } else if (et == T_NUM) { snprintf(numstr, sizeof(numstr), "%g", tod(elem)); @@ -6203,7 +6630,7 @@ static ant_value_t builtin_array_join(ant_t *js, ant_value_t *args, int nargs) { if (vtype(str_val) == T_STR) { ant_offset_t soff, slen; soff = vstr(js, str_val, &slen); - elem_str = (const char *)&js->mem[soff]; + elem_str = (const char *)(uintptr_t)(soff); elem_len = slen; } } @@ -6309,7 +6736,7 @@ static ant_value_t builtin_array_reverse(ant_t *js, ant_value_t *args, int nargs if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) return js_mkerr(js, "reverse called on non-array"); - if (is_proxy(js, arr)) { + if (is_proxy(arr)) { ant_offset_t len = proxy_aware_length(js, arr); if (len <= 1) return arr; ant_value_t read_from = proxy_read_target(js, arr); @@ -6338,49 +6765,35 @@ static ant_value_t builtin_array_reverse(ant_t *js, ant_value_t *args, int nargs ant_offset_t len = get_array_length(js, arr); if (len <= 1) return arr; - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { for (ant_offset_t i = 0; i < len / 2; i++) { - ant_value_t a = dense_get(js, doff, i); - ant_value_t b = dense_get(js, doff, len - 1 - i); + ant_value_t a = dense_get(doff, i); + ant_value_t b = dense_get(doff, len - 1 - i); dense_set(js, doff, i, b); dense_set(js, doff, len - 1 - i, a); } return arr; } - ant_value_t *vals = malloc(len * sizeof(ant_value_t)); - ant_offset_t *offs = malloc(len * sizeof(ant_offset_t)); - if (!vals || !offs) { free(vals); free(offs); return js_mkerr(js, "out of memory"); } + for (ant_offset_t lower = 0; lower < len / 2; lower++) { + ant_offset_t upper = len - lower - 1; + bool lower_exists = arr_has(js, arr, lower); + bool upper_exists = arr_has(js, arr, upper); + ant_value_t lower_val = lower_exists ? arr_get(js, arr, lower) : js_mkundef(); + ant_value_t upper_val = upper_exists ? arr_get(js, arr, upper) : js_mkundef(); - ant_offset_t count = 0; - ant_iter_t iter = js_prop_iter_begin(js, arr); - const char *key; - size_t key_len; - ant_value_t val; - - while (js_prop_iter_next(&iter, &key, &key_len, &val)) { - unsigned long parsed_idx; - if (!parse_array_index(key, key_len, len, &parsed_idx) || count >= len) continue; - - vals[count] = val; - offs[count] = iter.off; - count++; - } - js_prop_iter_end(&iter); - - for (ant_offset_t i = 0; i < count / 2; i++) { - ant_value_t tmp = vals[i]; - vals[i] = vals[count - 1 - i]; - vals[count - 1 - i] = tmp; - } - - for (ant_offset_t i = 0; i < count; i++) { - saveval(js, offs[i] + sizeof(ant_offset_t) * 2, vals[i]); + if (lower_exists && upper_exists) { + arr_set(js, arr, lower, upper_val); + arr_set(js, arr, upper, lower_val); + } else if (upper_exists) { + arr_set(js, arr, lower, upper_val); + arr_del(js, arr, upper); + } else if (lower_exists) { + arr_set(js, arr, upper, lower_val); + arr_del(js, arr, lower); + } } - - free(vals); - free(offs); return arr; } @@ -6517,7 +6930,7 @@ static ant_value_t builtin_array_concat(ant_t *js, ant_value_t *args, int nargs) if (vtype(arg) == T_ARR || vtype(arg) == T_OBJ) { bool array_default_spreadable = (vtype(arg) == T_ARR); - if (!array_default_spreadable && is_proxy(js, arg)) { + if (!array_default_spreadable && is_proxy(arg)) { ant_value_t target = proxy_read_target(js, arg); array_default_spreadable = (vtype(target) == T_ARR); } @@ -6711,7 +7124,7 @@ static ant_value_t builtin_array_flatMap(ant_t *js, ant_value_t *args, int nargs static const char *js_tostring(ant_t *js, ant_value_t v) { if (vtype(v) == T_STR) { ant_offset_t slen, off = vstr(js, v, &slen); - return (const char *)&js->mem[off]; + return (const char *)(uintptr_t)(off); } return js_str(js, v); } @@ -6727,8 +7140,8 @@ static int js_compare_values(ant_t *js, ant_value_t a, ant_value_t b, ant_value_ if (vtype(a) == T_STR && vtype(b) == T_STR) { ant_offset_t len_a, len_b; - const char *sa = (const char *)&js->mem[vstr(js, a, &len_a)]; - const char *sb = (const char *)&js->mem[vstr(js, b, &len_b)]; + const char *sa = (const char *)(uintptr_t)(vstr(js, a, &len_a)); + const char *sb = (const char *)(uintptr_t)(vstr(js, b, &len_b)); return strcmp(sa, sb); } @@ -6836,21 +7249,23 @@ static ant_value_t builtin_array_shift(ant_t *js, ant_value_t *args, int nargs) ant_offset_t len = proxy_aware_length(js, arr); if (len == 0) return js_mkundef(); - ant_offset_t doff = get_dense_buf(js, arr); - if (doff && !is_proxy(js, arr)) { - ant_offset_t d_len = dense_length(js, doff); + ant_offset_t doff = get_dense_buf(arr); + if (doff && !is_proxy(arr)) { + ant_offset_t d_len = dense_iterable_length(js, arr); + if (len != d_len) goto shift_slow; if (d_len == 0) return js_mkundef(); - ant_value_t first = dense_get(js, doff, 0); + ant_value_t *d = dense_data(doff); + if (!d) return js_mkundef(); + ant_value_t first = dense_get(doff, 0); if (is_empty_slot(first)) first = js_mkundef(); - memmove(&js->mem[doff + sizeof(ant_offset_t) * 2], - &js->mem[doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t)], - sizeof(ant_value_t) * (d_len - 1)); + memmove(&d[0], &d[1], sizeof(ant_value_t) * (size_t)(d_len - 1)); dense_set(js, doff, d_len - 1, T_EMPTY); - dense_set_length(js, doff, d_len - 1); + array_len_set(js, arr, len - 1); return first; } - ant_value_t read_from = is_proxy(js, arr) ? proxy_read_target(js, arr) : arr; + shift_slow: + ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr; ant_value_t first = arr_get(js, read_from, 0); for (ant_offset_t i = 1; i < len; i++) { @@ -6862,8 +7277,8 @@ static ant_value_t builtin_array_shift(ant_t *js, ant_value_t *args, int nargs) } } - js_setprop(js, arr, js->length_str, tov((double)(len - 1))); - js->needs_gc = true; + if (array_obj_ptr(arr)) array_len_set(js, arr, len - 1); + else js_setprop(js, arr, js->length_str, tov((double)(len - 1))); return first; } @@ -6876,25 +7291,27 @@ static ant_value_t builtin_array_unshift(ant_t *js, ant_value_t *args, int nargs ant_offset_t len = proxy_aware_length(js, arr); - ant_offset_t doff = get_dense_buf(js, arr); - if (doff && !is_proxy(js, arr)) { - ant_offset_t d_len = dense_length(js, doff); - ant_offset_t new_len = d_len + nargs; - ant_offset_t cap = dense_capacity(js, doff); + ant_offset_t doff = get_dense_buf(arr); + if (doff && !is_proxy(arr)) { + ant_offset_t d_len = dense_iterable_length(js, arr); + if (len != d_len) goto unshift_slow; + ant_offset_t new_len = len + nargs; + ant_offset_t cap = dense_capacity(doff); if (new_len > cap) { doff = dense_grow(js, arr, new_len); if (doff == 0) return js_mkerr(js, "oom"); } - memmove(&js->mem[doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * nargs], - &js->mem[doff + sizeof(ant_offset_t) * 2], - sizeof(ant_value_t) * d_len); + ant_value_t *d = dense_data(doff); + if (!d) return js_mkerr(js, "oom"); + memmove(&d[nargs], &d[0], sizeof(ant_value_t) * (size_t)d_len); for (int i = 0; i < nargs; i++) dense_set(js, doff, (ant_offset_t)i, args[i]); - dense_set_length(js, doff, new_len); + array_len_set(js, arr, new_len); return tov((double) new_len); } - ant_value_t read_from = is_proxy(js, arr) ? proxy_read_target(js, arr) : arr; + unshift_slow: + ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr; for (int i = (int)len - 1; i >= 0; i--) { if (arr_has(js, read_from, (ant_offset_t)i)) { @@ -6913,7 +7330,8 @@ static ant_value_t builtin_array_unshift(ant_t *js, ant_value_t *args, int nargs } ant_offset_t new_len = len + nargs; - js_setprop(js, arr, js->length_str, tov((double) new_len)); + if (array_obj_ptr(arr)) array_len_set(js, arr, new_len); + else js_setprop(js, arr, js->length_str, tov((double) new_len)); return tov((double) new_len); } @@ -6949,7 +7367,6 @@ static ant_value_t builtin_array_sort(ant_t *js, ant_value_t *args, int nargs) { ant_value_t arr = js->this_val; ant_value_t compareFn = js_mkundef(); ant_value_t *vals = NULL, *keys = NULL, *temp_vals = NULL, *temp_keys = NULL; - ant_offset_t *offs = NULL; ant_offset_t count = 0, undef_count = 0, len = 0; if (vtype(arr) != T_ARR && vtype(arr) != T_OBJ) @@ -6964,42 +7381,25 @@ static ant_value_t builtin_array_sort(ant_t *js, ant_value_t *args, int nargs) { len = get_array_length(js, arr); if (len == 0) return arr; - ant_offset_t doff = get_dense_buf(js, arr); + ant_offset_t doff = get_dense_buf(arr); if (doff) { vals = malloc(len * sizeof(ant_value_t)); if (!vals) goto oom; for (ant_offset_t i = 0; i < len; i++) { - ant_value_t v = dense_get(js, doff, i); + ant_value_t v = dense_get(doff, i); if (is_empty_slot(v) || vtype(v) == T_UNDEF) undef_count++; else vals[count++] = v; } } else { vals = malloc(len * sizeof(ant_value_t)); - offs = malloc(len * sizeof(ant_offset_t)); - if (!vals || !offs) goto oom; - - ant_iter_t iter = js_prop_iter_begin(js, arr); - const char *key; - size_t key_len; - ant_value_t val; - - while (js_prop_iter_next(&iter, &key, &key_len, &val)) { - if (key_len == 0 || key[0] > '9' || key[0] < '0') continue; - - unsigned idx = 0; - bool valid = true; - for (size_t i = 0; i < key_len && valid; i++) { - if (key[i] < '0' || key[i] > '9') valid = false; - else idx = idx * 10 + (key[i] - '0'); - } - if (!valid || idx >= len || (count + undef_count) >= len) continue; - - offs[count + undef_count] = iter.off; - if (vtype(val) == T_UNDEF) undef_count++; - else vals[count++] = val; + if (!vals) goto oom; + + for (ant_offset_t i = 0; i < len; i++) { + if (!arr_has(js, arr, i)) continue; + ant_value_t v = arr_get(js, arr, i); + if (vtype(v) == T_UNDEF) undef_count++; + else vals[count++] = v; } - - js_prop_iter_end(&iter); } if (count <= 1) goto writeback; @@ -7028,8 +7428,8 @@ static ant_value_t builtin_array_sort(ant_t *js, ant_value_t *args, int nargs) { int cmp; if (use_keys) { ant_offset_t len_a, len_b; - const char *sa = (const char *)&js->mem[vstr(js, keys[i], &len_a)]; - const char *sb = (const char *)&js->mem[vstr(js, keys[j], &len_b)]; + const char *sa = (const char *)(uintptr_t)(vstr(js, keys[i], &len_a)); + const char *sb = (const char *)(uintptr_t)(vstr(js, keys[j], &len_b)); cmp = strcmp(sa, sb); } else { cmp = js_compare_values(js, vals[i], vals[j], compareFn); @@ -7066,17 +7466,16 @@ writeback: for (ant_offset_t i = count; i < count + undef_count; i++) dense_set(js, doff, i, js_mkundef()); for (ant_offset_t i = count + undef_count; i < len; i++) dense_set(js, doff, i, T_EMPTY); } else { - for (ant_offset_t i = 0; i < count; i++) - saveval(js, offs[i] + sizeof(ant_offset_t) * 2, vals[i]); - for (ant_offset_t i = 0; i < undef_count; i++) - saveval(js, offs[count + i] + sizeof(ant_offset_t) * 2, js_mkundef()); + ant_offset_t out = 0; + for (; out < count; out++) arr_set(js, arr, out, vals[out]); + for (ant_offset_t i = 0; i < undef_count; i++, out++) arr_set(js, arr, out, js_mkundef()); + for (; out < len; out++) arr_del(js, arr, out); } free(temp_keys); free(temp_vals); free(keys); free(vals); - free(offs); return arr; oom: @@ -7084,7 +7483,6 @@ oom: free(temp_vals); free(keys); free(vals); - free(offs); return js_mkerr(js, "out of memory"); } @@ -7095,7 +7493,7 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) } ant_offset_t len = proxy_aware_length(js, arr); - ant_value_t read_from = is_proxy(js, arr) ? proxy_read_target(js, arr) : arr; + ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr; int start = 0; if (nargs >= 1 && vtype(args[0]) == T_NUM) { @@ -7117,30 +7515,29 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) ant_value_t removed = array_alloc_like(js, arr); if (is_err(removed)) return removed; - ant_offset_t doff = get_dense_buf(js, arr); - if (doff && !is_proxy(js, arr)) { + ant_offset_t doff = get_dense_buf(arr); + if (doff && !is_proxy(arr)) { + ant_offset_t d_len = dense_iterable_length(js, arr); + if (d_len != len) goto splice_slow; for (int i = 0; i < deleteCount; i++) { ant_value_t elem = arr_get(js, arr, (ant_offset_t)(start + i)); arr_set(js, removed, (ant_offset_t)i, elem); } - ant_offset_t d_len = dense_length(js, doff); int shift = insertCount - deleteCount; ant_offset_t new_len = (ant_offset_t)((int)d_len + shift); if (shift != 0) { - if (new_len > dense_capacity(js, doff)) { + if (new_len > dense_capacity(doff)) { doff = dense_grow(js, arr, new_len); if (doff == 0) return js_mkerr(js, "oom"); } ant_offset_t move_start = (ant_offset_t)(start + deleteCount); ant_offset_t move_dest = (ant_offset_t)(start + insertCount); ant_offset_t move_count = d_len - move_start; - if (move_count > 0) memmove( - &js->mem[doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * move_dest], - &js->mem[doff + sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * move_start], - sizeof(ant_value_t) * move_count - ); + ant_value_t *d = dense_data(doff); + if (!d) return js_mkerr(js, "oom"); + if (move_count > 0) memmove(&d[move_dest], &d[move_start], sizeof(ant_value_t) * (size_t)move_count); } for (int i = 0; i < insertCount; i++) @@ -7151,18 +7548,18 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) dense_set(js, doff, i, T_EMPTY); } - dense_set_length(js, doff, new_len); - if (deleteCount > 0) js->needs_gc = true; + array_len_set(js, arr, new_len); return removed; } + splice_slow: for (int i = 0; i < deleteCount; i++) { char src[16], dst[16]; snprintf(src, sizeof(src), "%u", (unsigned)(start + i)); snprintf(dst, sizeof(dst), "%u", (unsigned) i); ant_offset_t elem_off = lkp(js, read_from, src, strlen(src)); if (elem_off != 0) { - ant_value_t elem = resolveprop(js, mkval(T_PROP, elem_off)); + ant_value_t elem = propref_load(js, elem_off); ant_value_t key = js_mkstr(js, dst, strlen(dst)); js_setprop(js, removed, key, elem); } @@ -7177,7 +7574,7 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) snprintf(src, sizeof(src), "%u", (unsigned) i); snprintf(dst, sizeof(dst), "%u", (unsigned)(i + shift)); ant_offset_t elem_off = lkp(js, read_from, src, strlen(src)); - ant_value_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); + ant_value_t elem = elem_off ? propref_load(js, elem_off) : js_mkundef(); ant_value_t key = js_mkstr(js, dst, strlen(dst)); js_setprop(js, arr, key, elem); } @@ -7187,7 +7584,7 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) snprintf(src, sizeof(src), "%u", (unsigned) i); snprintf(dst, sizeof(dst), "%u", (unsigned)(i + shift)); ant_offset_t elem_off = lkp(js, read_from, src, strlen(src)); - ant_value_t elem = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); + ant_value_t elem = elem_off ? propref_load(js, elem_off) : js_mkundef(); ant_value_t key = js_mkstr(js, dst, strlen(dst)); js_setprop(js, arr, key, elem); } @@ -7200,8 +7597,8 @@ static ant_value_t builtin_array_splice(ant_t *js, ant_value_t *args, int nargs) js_setprop(js, arr, key, args[2 + i]); } - js_setprop(js, arr, js->length_str, tov((double)((int)len + shift))); - if (deleteCount > 0) js->needs_gc = true; + if (array_obj_ptr(arr)) array_len_set(js, arr, (ant_offset_t)((int)len + shift)); + else js_setprop(js, arr, js->length_str, tov((double)((int)len + shift))); return removed; } @@ -7213,7 +7610,7 @@ static ant_value_t builtin_array_copyWithin(ant_t *js, ant_value_t *args, int na } ant_offset_t len = proxy_aware_length(js, arr); - ant_value_t read_from = is_proxy(js, arr) ? proxy_read_target(js, arr) : arr; + ant_value_t read_from = is_proxy(arr) ? proxy_read_target(js, arr) : arr; int target = 0, start = 0, end = (int)len; if (nargs >= 1 && vtype(args[0]) == T_NUM) { @@ -7237,16 +7634,16 @@ static ant_value_t builtin_array_copyWithin(ant_t *js, ant_value_t *args, int na if (count > (int)len - target) count = (int)len - target; if (count <= 0) return arr; - ant_offset_t doff = get_dense_buf(js, arr); - if (doff && !is_proxy(js, arr)) { + ant_offset_t doff = get_dense_buf(arr); + if (doff && !is_proxy(arr)) { if (start < target) { for (int i = count - 1; i >= 0; i--) { - ant_value_t v = dense_get(js, doff, (ant_offset_t)(start + i)); + ant_value_t v = dense_get(doff, (ant_offset_t)(start + i)); dense_set(js, doff, (ant_offset_t)(target + i), is_empty_slot(v) ? js_mkundef() : v); } } else { for (int i = 0; i < count; i++) { - ant_value_t v = dense_get(js, doff, (ant_offset_t)(start + i)); + ant_value_t v = dense_get(doff, (ant_offset_t)(start + i)); dense_set(js, doff, (ant_offset_t)(target + i), is_empty_slot(v) ? js_mkundef() : v); } } @@ -7258,7 +7655,7 @@ static ant_value_t builtin_array_copyWithin(ant_t *js, ant_value_t *args, int na char idxstr[16]; size_t idxlen = uint_to_str(idxstr, sizeof(idxstr), (unsigned)(start + i)); ant_offset_t elem_off = lkp(js, read_from, idxstr, idxlen); - temp[i] = elem_off ? resolveprop(js, mkval(T_PROP, elem_off)) : js_mkundef(); + temp[i] = elem_off ? propref_load(js, elem_off) : js_mkundef(); } for (int i = 0; i < count; i++) { @@ -7525,19 +7922,23 @@ static ant_value_t builtin_Array_from(ant_t *js, ant_value_t *args, int nargs) { ant_value_t result = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, 0) : mkarr(js); if (is_err(result)) return result; - bool result_is_proxy = is_proxy(js, result); + bool result_is_proxy = is_proxy(result); ant_value_t write_target = result_is_proxy ? proxy_read_target(js, result) : result; ant_value_t iter_sym = get_iterator_sym(); if (vtype(src) == T_STR) { - if (is_rope(js, src)) { src = rope_flatten(js, src); if (is_err(src)) return src; } + if (str_is_heap_rope(src)) { src = rope_flatten(js, src); if (is_err(src)) return src; } ant_offset_t slen = str_len_fast(js, src); array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; for (ant_offset_t i = 0; i < slen; ) { ant_offset_t off = vstr(js, src, NULL); utf8proc_int32_t cp; - ant_offset_t cb_len = (ant_offset_t)utf8_next((const utf8proc_uint8_t *)&js->mem[off + i], (utf8proc_ssize_t)(slen - i), &cp); - ant_value_t ch = js_mkstr(js, (char *)&js->mem[off + i], cb_len); + ant_offset_t cb_len = (ant_offset_t)utf8_next( + (const utf8proc_uint8_t *)(uintptr_t)(off + i), + (utf8proc_ssize_t)(slen - i), + &cp + ); + ant_value_t ch = js_mkstr(js, (const void *)(uintptr_t)(off + i), cb_len); ant_value_t out; iter_action_t act = array_from_iter_cb(js, ch, &ctx, &out); @@ -7548,7 +7949,7 @@ static ant_value_t builtin_Array_from(ant_t *js, ant_value_t *args, int nargs) { if (vtype(result) != T_ARR) js_setprop(js, result, js->length_str, tov((double)ctx.index)); } else if (vtype(src) == T_ARR) { ant_offset_t iter_off = (vtype(iter_sym) == T_SYMBOL) ? lkp_sym_proto(js, src, (ant_offset_t)vdata(iter_sym)) : 0; - bool default_iter = iter_off != 0 && vtype(loadval(js, iter_off + sizeof(ant_offset_t) * 2)) == T_CFUNC; + bool default_iter = iter_off != 0 && vtype(propref_load(js, iter_off)) == T_CFUNC; if (default_iter) { array_from_iter_ctx_t ctx = { write_target, result, mapFn, mapThis, 0 }; @@ -7595,7 +7996,7 @@ static ant_value_t builtin_Array_of(ant_t *js, ant_value_t *args, int nargs) { ant_value_t arr = use_ctor ? array_alloc_from_ctor_with_length(js, ctor, (ant_offset_t)nargs) : mkarr(js); if (is_err(arr)) return arr; - bool arr_is_proxy = is_proxy(js, arr); + bool arr_is_proxy = is_proxy(arr); ant_value_t write_target = arr_is_proxy ? proxy_read_target(js, arr) : arr; for (int i = 0; i < nargs; i++) { @@ -7635,8 +8036,8 @@ static ant_value_t builtin_string_indexOf(ant_t *js, ant_value_t *args, int narg if (search_len == 0) return tov(D(start)); if (start + search_len > str_len) return tov(-1); - const char *str_ptr = (char *) &js->mem[str_off]; - const char *search_ptr = (char *) &js->mem[search_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *search_ptr = (char *)(uintptr_t)(search_off); for (ant_offset_t i = start; i <= str_len - search_len; i++) { if (memcmp(str_ptr + i, search_ptr, search_len) == 0) return tov(D(i)); @@ -7648,7 +8049,7 @@ static ant_value_t builtin_string_substring(ant_t *js, ant_value_t *args, int na ant_value_t str = to_string_val(js, js->this_val); if (vtype(str) != T_STR) return js_mkerr(js, "substring called on non-string"); ant_offset_t byte_len, str_off = vstr(js, str, &byte_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t utf16_len = utf16_strlen(str_ptr, byte_len); ant_offset_t start = 0, end = (ant_offset_t)utf16_len; double dstr_len2 = D(utf16_len); @@ -7678,7 +8079,7 @@ static ant_value_t builtin_string_substr(ant_t *js, ant_value_t *args, int nargs ant_value_t str = to_string_val(js, js->this_val); if (vtype(str) != T_STR) return js_mkerr(js, "substr called on non-string"); ant_offset_t byte_len, str_off = vstr(js, str, &byte_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t utf16_len = utf16_strlen(str_ptr, byte_len); if (nargs < 1) return js_mkstr(js, str_ptr, byte_len); @@ -7709,7 +8110,7 @@ static ant_value_t builtin_string_substr(ant_t *js, ant_value_t *args, int nargs static ant_value_t string_split_impl(ant_t *js, ant_value_t str, ant_value_t *args, int nargs) { if (vtype(str) != T_STR) return js_mkerr(js, "split called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_value_t arr = mkarr(js); if (is_err(arr)) return arr; @@ -7730,11 +8131,11 @@ static ant_value_t string_split_impl(ant_t *js, ant_value_t str, ant_value_t *ar if (vtype(sep_arg) == T_OBJ) { ant_offset_t source_off = lkp(js, sep_arg, "source", 6); if (source_off == 0) goto return_whole; - ant_value_t source_val = resolveprop(js, mkval(T_PROP, source_off)); + ant_value_t source_val = propref_load(js, source_off); if (vtype(source_val) != T_STR) goto return_whole; ant_offset_t plen, poff = vstr(js, source_val, &plen); - const char *pattern_ptr = (char *) &js->mem[poff]; + const char *pattern_ptr = (char *)(uintptr_t)(poff); if (plen == 0 || (plen == 4 && memcmp(pattern_ptr, "(?:)", 4) == 0)) { ant_offset_t idx = 0; @@ -7851,7 +8252,7 @@ static ant_value_t string_split_impl(ant_t *js, ant_value_t str, ant_value_t *ar if (vtype(sep_arg) != T_STR) goto return_whole; ant_offset_t sep_len, sep_off = vstr(js, sep_arg, &sep_len); - const char *sep_ptr = (char *) &js->mem[sep_off]; + const char *sep_ptr = (char *)(uintptr_t)(sep_off); ant_offset_t idx = 0, start = 0; if (sep_len == 0) { @@ -7915,7 +8316,7 @@ static ant_value_t builtin_string_slice(ant_t *js, ant_value_t *args, int nargs) if (is_err(str)) return str; ant_offset_t byte_len, str_off = vstr(js, str, &byte_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t utf16_len = utf16_strlen(str_ptr, byte_len); ant_offset_t start = 0, end = (ant_offset_t)utf16_len; double dstr_len = D(utf16_len); @@ -7955,8 +8356,8 @@ static ant_value_t builtin_string_includes(ant_t *js, ant_value_t *args, int nar ant_offset_t str_len, str_off = vstr(js, str, &str_len); ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *str_ptr = (char *) &js->mem[str_off]; - const char *search_ptr = (char *) &js->mem[search_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *search_ptr = (char *)(uintptr_t)(search_off); ant_offset_t start = 0; if (nargs >= 2) { @@ -7990,8 +8391,8 @@ static ant_value_t builtin_string_startsWith(ant_t *js, ant_value_t *args, int n ant_offset_t str_len, str_off = vstr(js, str, &str_len); ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *str_ptr = (char *) &js->mem[str_off]; - const char *search_ptr = (char *) &js->mem[search_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *search_ptr = (char *)(uintptr_t)(search_off); if (search_len > str_len) return mkval(T_BOOL, 0); if (search_len == 0) return mkval(T_BOOL, 1); @@ -8015,8 +8416,8 @@ static ant_value_t builtin_string_endsWith(ant_t *js, ant_value_t *args, int nar ant_offset_t str_len, str_off = vstr(js, str, &str_len); ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *str_ptr = (char *) &js->mem[str_off]; - const char *search_ptr = (char *) &js->mem[search_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *search_ptr = (char *)(uintptr_t)(search_off); if (search_len > str_len) return mkval(T_BOOL, 0); if (search_len == 0) return mkval(T_BOOL, 1); @@ -8046,7 +8447,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg ant_value_t replacement = args[1]; ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); bool is_regex = false; bool global_flag = false; @@ -8058,7 +8459,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg ant_offset_t pattern_off = lkp(js, search, "source", 6); if (pattern_off == 0) goto not_regex; - ant_value_t pattern_val = resolveprop(js, mkval(T_PROP, pattern_off)); + ant_value_t pattern_val = propref_load(js, pattern_off); if (vtype(pattern_val) != T_STR) goto not_regex; is_regex = true; @@ -8066,17 +8467,17 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg pattern_len = plen; pattern_buf = (char *)ant_calloc(plen + 1); if (!pattern_buf) return js_mkerr(js, "oom"); - memcpy(pattern_buf, &js->mem[poff], plen); + memcpy(pattern_buf, (const void *)(uintptr_t)poff, plen); pattern_buf[plen] = '\0'; ant_offset_t flags_off = lkp(js, search, "flags", 5); if (flags_off == 0) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } - ant_value_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); + ant_value_t flags_val = propref_load(js, flags_off); if (vtype(flags_val) != T_STR) { free(pattern_buf); pattern_buf = NULL; goto not_regex; } ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *) &js->mem[foff]; + const char *flags_str = (char *)(uintptr_t)(foff); for (ant_offset_t i = 0; i < flen; i++) { if (flags_str[i] == 'g') global_flag = true; } @@ -8089,7 +8490,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg if (vtype(replacement) != T_STR) { if (pattern_buf) free(pattern_buf); return str; } ant_offset_t repl_off; repl_off = vstr(js, replacement, &repl_len); - repl_ptr = (char *) &js->mem[repl_off]; + repl_ptr = (char *)(uintptr_t)(repl_off); } size_t result_cap = str_len + repl_len + 256; @@ -8172,7 +8573,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg if (vtype(cb_result) == T_STR) { ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); ENSURE_RESULT_CAP(cb_len); - memcpy(result + result_len, &js->mem[cb_off], cb_len); + memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); result_len += cb_len; } else { char numbuf[32]; @@ -8220,7 +8621,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg } else { if (vtype(search) != T_STR) { free(result); return str; } ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *search_ptr = (char *) &js->mem[search_off]; + const char *search_ptr = (char *)(uintptr_t)(search_off); if (search_len > str_len) { free(result); return str; } @@ -8240,7 +8641,7 @@ static ant_value_t builtin_string_replace(ant_t *js, ant_value_t *args, int narg if (vtype(cb_result) == T_STR) { ant_offset_t cb_len, cb_off = vstr(js, cb_result, &cb_len); ENSURE_RESULT_CAP(cb_len); - memcpy(result + result_len, &js->mem[cb_off], cb_len); + memcpy(result + result_len, (const void *)(uintptr_t)cb_off, cb_len); result_len += cb_len; } else { char numbuf[32]; @@ -8285,13 +8686,13 @@ static ant_value_t builtin_string_replaceAll(ant_t *js, ant_value_t *args, int n if (vtype(replacement) != T_STR) return js_mkerr(js, "replaceAll requires string replacement"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_offset_t search_len, search_off = vstr(js, search, &search_len); - const char *search_ptr = (char *) &js->mem[search_off]; + const char *search_ptr = (char *)(uintptr_t)(search_off); ant_offset_t repl_len, repl_off = vstr(js, replacement, &repl_len); - const char *repl_ptr = (char *) &js->mem[repl_off]; + const char *repl_ptr = (char *)(uintptr_t)(repl_off); if (search_len == 0) { size_t total_len = str_len + (str_len + 1) * repl_len; @@ -8355,7 +8756,7 @@ static ant_value_t builtin_string_template(ant_t *js, ant_value_t *args, int nar ant_value_t data = args[0]; ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t result_cap = str_len + 256; size_t result_len = 0; @@ -8384,11 +8785,11 @@ static ant_value_t builtin_string_template(ant_t *js, ant_value_t *args, int nar ant_offset_t prop_off = lkp(js, data, str_ptr + start, key_len); if (prop_off != 0) { - ant_value_t value = resolveprop(js, mkval(T_PROP, prop_off)); + ant_value_t value = propref_load(js, prop_off); if (vtype(value) == T_STR) { ant_offset_t val_len, val_off = vstr(js, value, &val_len); ENSURE_CAP(val_len); - memcpy(result + result_len, &js->mem[val_off], val_len); + memcpy(result + result_len, (const void *)(uintptr_t)val_off, val_len); result_len += val_len; } else if (vtype(value) == T_NUM) { char numstr[32]; @@ -8430,7 +8831,7 @@ static ant_value_t builtin_string_charCodeAt(ant_t *js, ant_value_t *args, int n if (idx_l < 0) return tov(JS_NAN); ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); uint32_t code_unit = utf16_code_unit_at(str_data, byte_len, idx_l); if (code_unit == 0xFFFFFFFF) return tov(JS_NAN); @@ -8451,7 +8852,7 @@ static ant_value_t builtin_string_codePointAt(ant_t *js, ant_value_t *args, int ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); uint32_t cp = utf16_codepoint_at(str_data, byte_len, idx_l); if (cp == 0xFFFFFFFF) return js_mkundef(); @@ -8465,7 +8866,7 @@ static ant_value_t builtin_string_toLowerCase(ant_t *js, ant_value_t *args, int if (vtype(str) != T_STR) return js_mkerr(js, "toLowerCase called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); if (str_len == 0) return js_mkstr(js, "", 0); const utf8proc_uint8_t *src = (const utf8proc_uint8_t *)str_ptr; @@ -8485,7 +8886,7 @@ static ant_value_t builtin_string_toLowerCase(ant_t *js, ant_value_t *args, int ant_value_t result = js_mkstr(js, NULL, out_len); if (is_err(result)) return result; ant_offset_t result_len, result_off = vstr(js, result, &result_len); - char *result_ptr = (char *) &js->mem[result_off]; + char *result_ptr = (char *)(uintptr_t)(result_off); pos = 0; ant_offset_t wpos = 0; @@ -8506,7 +8907,7 @@ static ant_value_t builtin_string_toUpperCase(ant_t *js, ant_value_t *args, int if (vtype(str) != T_STR) return js_mkerr(js, "toUpperCase called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); if (str_len == 0) return js_mkstr(js, "", 0); const utf8proc_uint8_t *src = (const utf8proc_uint8_t *)str_ptr; @@ -8526,7 +8927,7 @@ static ant_value_t builtin_string_toUpperCase(ant_t *js, ant_value_t *args, int ant_value_t result = js_mkstr(js, NULL, out_len); if (is_err(result)) return result; ant_offset_t result_len, result_off = vstr(js, result, &result_len); - char *result_ptr = (char *) &js->mem[result_off]; + char *result_ptr = (char *)(uintptr_t)(result_off); pos = 0; ant_offset_t wpos = 0; @@ -8547,7 +8948,7 @@ static ant_value_t builtin_string_trim(ant_t *js, ant_value_t *args, int nargs) if (vtype(str) != T_STR) return js_mkerr(js, "trim called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_offset_t start = 0, end = str_len; while (start < end && is_space(str_ptr[start])) start++; @@ -8562,7 +8963,7 @@ static ant_value_t builtin_string_trimStart(ant_t *js, ant_value_t *args, int na if (vtype(str) != T_STR) return js_mkerr(js, "trimStart called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_offset_t start = 0; while (start < str_len && is_space(str_ptr[start])) start++; @@ -8576,7 +8977,7 @@ static ant_value_t builtin_string_trimEnd(ant_t *js, ant_value_t *args, int narg if (vtype(str) != T_STR) return js_mkerr(js, "trimEnd called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_offset_t end = str_len; while (end > 0 && is_space(str_ptr[end - 1])) end--; @@ -8594,7 +8995,7 @@ static ant_value_t builtin_string_repeat(ant_t *js, ant_value_t *args, int nargs ant_offset_t count = (ant_offset_t) count_d; ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); if (count == 0 || str_len == 0) return js_mkstr(js, "", 0); @@ -8602,7 +9003,7 @@ static ant_value_t builtin_string_repeat(ant_t *js, ant_value_t *args, int nargs if (is_err(result)) return result; ant_offset_t result_len, result_off = vstr(js, result, &result_len); - char *result_ptr = (char *) &js->mem[result_off]; + char *result_ptr = (char *)(uintptr_t)(result_off); for (ant_offset_t i = 0; i < count; i++) { memcpy(result_ptr + i * str_len, str_ptr, str_len); @@ -8619,7 +9020,7 @@ static ant_value_t builtin_string_padStart(ant_t *js, ant_value_t *args, int nar ant_offset_t target_len = (ant_offset_t)tod(args[0]); if (target_len <= 0) return str; ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t str_utf16_len = utf16_strlen(str_ptr, (size_t)str_len); if ((size_t)target_len <= str_utf16_len) return str; @@ -8630,7 +9031,7 @@ static ant_value_t builtin_string_padStart(ant_t *js, ant_value_t *args, int nar if (is_err(pad_val)) return pad_val; } ant_offset_t pad_len, pad_off = vstr(js, pad_val, &pad_len); - const char *pad_str = (char *)&js->mem[pad_off]; + const char *pad_str = (char *)(uintptr_t)(pad_off); size_t pad_utf16_len = utf16_strlen(pad_str, (size_t)pad_len); if (pad_utf16_len == 0) return str; @@ -8651,7 +9052,7 @@ static ant_value_t builtin_string_padStart(ant_t *js, ant_value_t *args, int nar if (is_err(result)) return result; ant_offset_t result_len, result_off = vstr(js, result, &result_len); - char *result_ptr = (char *) &js->mem[result_off]; + char *result_ptr = (char *)(uintptr_t)(result_off); size_t pos = 0; for (size_t i = 0; i < full_repeats; i++) { @@ -8675,7 +9076,7 @@ static ant_value_t builtin_string_padEnd(ant_t *js, ant_value_t *args, int nargs ant_offset_t target_len = (ant_offset_t)tod(args[0]); if (target_len <= 0) return str; ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *) &js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); size_t str_utf16_len = utf16_strlen(str_ptr, (size_t)str_len); if ((size_t)target_len <= str_utf16_len) return str; @@ -8686,7 +9087,7 @@ static ant_value_t builtin_string_padEnd(ant_t *js, ant_value_t *args, int nargs if (is_err(pad_val)) return pad_val; } ant_offset_t pad_len, pad_off = vstr(js, pad_val, &pad_len); - const char *pad_str = (char *) &js->mem[pad_off]; + const char *pad_str = (char *)(uintptr_t)(pad_off); size_t pad_utf16_len = utf16_strlen(pad_str, (size_t)pad_len); if (pad_utf16_len == 0) return str; @@ -8707,7 +9108,7 @@ static ant_value_t builtin_string_padEnd(ant_t *js, ant_value_t *args, int nargs if (is_err(result)) return result; ant_offset_t result_len, result_off = vstr(js, result, &result_len); - char *result_ptr = (char *) &js->mem[result_off]; + char *result_ptr = (char *)(uintptr_t)(result_off); memcpy(result_ptr, str_ptr, (size_t)str_len); size_t pos = (size_t)str_len; @@ -8736,7 +9137,7 @@ static ant_value_t builtin_string_charAt(ant_t *js, ant_value_t *args, int nargs ant_offset_t idx = (ant_offset_t) idx_d; ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); size_t char_bytes; int byte_offset = utf16_index_to_byte_offset(str_data, byte_len, idx, &char_bytes); @@ -8753,7 +9154,7 @@ static ant_value_t builtin_string_at(ant_t *js, ant_value_t *args, int nargs) { if (isnan(idx_d) || isinf(idx_d)) return js_mkundef(); ant_offset_t byte_len; ant_offset_t str_off = vstr(js, str, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); size_t utf16_len = utf16_strlen(str_data, byte_len); long idx = (long) idx_d; @@ -8781,8 +9182,8 @@ static ant_value_t builtin_string_localeCompare(ant_t *js, ant_value_t *args, in ant_offset_t str_len, str_off = vstr(js, str, &str_len); ant_offset_t that_len, that_off = vstr(js, that, &that_len); - const char *str_ptr = (char *)&js->mem[str_off]; - const char *that_ptr = (char *)&js->mem[that_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *that_ptr = (char *)(uintptr_t)(that_off); int result = strcoll(str_ptr, that_ptr); if (result < 0) return tov(-1); @@ -8814,8 +9215,8 @@ static ant_value_t builtin_string_lastIndexOf(ant_t *js, ant_value_t *args, int if (search_len == 0) return tov((double) (max_start > str_len ? str_len : max_start)); if (search_len > str_len) return tov(-1); - const char *str_ptr = (char *) &js->mem[str_off]; - const char *search_ptr = (char *) &js->mem[search_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); + const char *search_ptr = (char *)(uintptr_t)(search_off); ant_offset_t start = (max_start + search_len > str_len) ? str_len - search_len : max_start; for (ant_offset_t i = start + 1; i > 0; i--) { @@ -8856,12 +9257,12 @@ static ant_value_t builtin_string_concat(ant_t *js, ant_value_t *args, int nargs ant_offset_t base_len; base_off = vstr(js, str, &base_len); - memcpy(result, &js->mem[base_off], base_len); + memcpy(result, (const void *)(uintptr_t)base_off, base_len); ant_offset_t pos = base_len; for (int i = 0; i < nargs; i++) { ant_offset_t arg_len, arg_off = vstr(js, str_args[i], &arg_len); - memcpy(result + pos, &js->mem[arg_off], arg_len); + memcpy(result + pos, (const void *)(uintptr_t)arg_off, arg_len); pos += arg_len; } result[pos] = '\0'; @@ -8877,7 +9278,7 @@ static ant_value_t builtin_string_normalize(ant_t *js, ant_value_t *args, int na if (vtype(str) != T_STR) return js_mkerr(js, "normalize called on non-string"); ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (const char *)&js->mem[str_off]; + const char *str_ptr = (const char *)(uintptr_t)(str_off); if (str_len == 0) return js_mkstr(js, "", 0); utf8proc_option_t opts = UTF8PROC_COMPOSE | UTF8PROC_STABLE; @@ -8886,7 +9287,7 @@ static ant_value_t builtin_string_normalize(ant_t *js, ant_value_t *args, int na ant_value_t form_val = js_tostring_val(js, args[0]); if (is_err(form_val)) return form_val; ant_offset_t flen, foff = vstr(js, form_val, &flen); - const char *form = (const char *)&js->mem[foff]; + const char *form = (const char *)(uintptr_t)(foff); if (flen == 3 && memcmp(form, "NFC", 3) == 0) { opts = UTF8PROC_COMPOSE | UTF8PROC_STABLE; @@ -8998,7 +9399,7 @@ static bool string_builder_append_value( *cap = next; } - if (slen > 0) memcpy(*buf + *len, &js->mem[soff], (size_t)slen); + if (slen > 0) memcpy(*buf + *len, (const void *)(uintptr_t)soff, (size_t)slen); *len += (size_t)slen; (*buf)[*len] = '\0'; return true; @@ -9443,7 +9844,7 @@ static ant_value_t builtin_parseInt(ant_t *js, ant_value_t *args, int nargs) { } ant_offset_t str_len, str_off = vstr(js, str_val, &str_len); - const char *str = (char *) &js->mem[str_off]; + const char *str = (char *)(uintptr_t)(str_off); int radix = 0; if (nargs >= 2 && vtype(args[1]) == T_NUM) { @@ -9508,7 +9909,7 @@ static ant_value_t builtin_parseFloat(ant_t *js, ant_value_t *args, int nargs) { } ant_offset_t str_len, str_off = vstr(js, str_val, &str_len); - const char *str = (char *) &js->mem[str_off]; + const char *str = (char *)(uintptr_t)(str_off); ant_offset_t i = 0; while (i < str_len && is_space(str[i])) i++; @@ -9533,7 +9934,7 @@ static ant_value_t builtin_btoa(ant_t *js, ant_value_t *args, int nargs) { } ant_offset_t str_len, str_off = vstr(js, str_val, &str_len); - const char *str = (char *) &js->mem[str_off]; + const char *str = (char *)(uintptr_t)(str_off); size_t out_len; char *out = ant_base64_encode((const uint8_t *)str, str_len, &out_len); @@ -9555,7 +9956,7 @@ static ant_value_t builtin_atob(ant_t *js, ant_value_t *args, int nargs) { } ant_offset_t str_len, str_off = vstr(js, str_val, &str_len); - const char *str = (char *) &js->mem[str_off]; + const char *str = (char *)(uintptr_t)(str_off); if (str_len == 0) return js_mkstr(js, "", 0); size_t out_len; @@ -9575,7 +9976,7 @@ static void reject_promise(ant_t *js, ant_value_t p, ant_value_t val); static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len) { uint32_t pid = get_promise_id(js, value); - promise_data_entry_t *pd = get_promise_data(pid, false); + ant_promise_state_t *pd = get_promise_data(js, value, false); const char *content; char *allocated = NULL; @@ -9590,76 +9991,102 @@ static size_t strpromise(ant_t *js, ant_value_t value, char *buf, size_t len) { content = allocated; } else { content = allocated = tostr_alloc(js, pd->value); } - size_t result = (pd && pd->trigger_pid) - ? (size_t)snprintf(buf, len, "Promise {\n %s,\n Symbol(async_id): %u,\n Symbol(trigger_async_id): %u\n}", content, pid, pd->trigger_pid) + uint32_t trigger_pid = 0; + if (pd && vtype(pd->trigger_parent) == T_PROMISE) { + trigger_pid = get_promise_id(js, pd->trigger_parent); + } + size_t result = trigger_pid + ? (size_t)snprintf(buf, len, "Promise {\n %s,\n Symbol(async_id): %u,\n Symbol(trigger_async_id): %u\n}", content, pid, trigger_pid) : (size_t)snprintf(buf, len, "Promise {\n %s,\n Symbol(async_id): %u\n}", content, pid); if (allocated) free(allocated); return result; } -static promise_data_entry_t *get_promise_data(uint32_t promise_id, bool create) { - promise_data_entry_t *entry = NULL; - HASH_FIND(hh, promise_registry, &promise_id, sizeof(uint32_t), entry); - if (entry) return entry; +static ant_promise_state_t *get_promise_data(ant_t *js, ant_value_t promise, bool create) { + if (vtype(promise) != T_PROMISE) return NULL; + ant_object_t *obj = js_obj_ptr(js_as_obj(promise)); + if (!obj) return NULL; + if (obj->promise_state) return obj->promise_state; if (!create) return NULL; - - entry = (promise_data_entry_t *)malloc(sizeof(promise_data_entry_t)); - entry->promise_id = promise_id; - entry->trigger_pid = 0; - entry->obj_offset = 0; + + ant_promise_state_t *entry = (ant_promise_state_t *)calloc(1, sizeof(*entry)); + if (!entry) return NULL; + entry->promise_id = next_promise_id++; + entry->trigger_parent = js_mkundef(); entry->state = 0; entry->value = js_mkundef(); entry->has_rejection_handler = false; + entry->processing = false; + entry->unhandled_reported = false; utarray_new(entry->handlers, &promise_handler_icd); - HASH_ADD(hh, promise_registry, promise_id, sizeof(uint32_t), entry); - + obj->promise_state = entry; + obj->type_tag = T_PROMISE; return entry; } static uint32_t get_promise_id(ant_t *js, ant_value_t p) { - ant_value_t p_obj = js_as_obj(p); - ant_value_t pid_val = get_slot(js, p_obj, SLOT_PID); - if (vtype(pid_val) == T_UNDEF) return 0; - return (uint32_t)tod(pid_val); + ant_promise_state_t *pd = get_promise_data(js, p, false); + return pd ? pd->promise_id : 0; +} + +static ant_value_t make_data_cfunc( + ant_t *js, ant_value_t data, + ant_value_t (*fn)(ant_t *, ant_value_t *, int) +) { + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, data); + + ant_value_t obj = mkobj(js, 0); + if (is_err(obj)) { + GC_ROOT_RESTORE(js, root_mark); + return obj; + } + + GC_ROOT_PIN(js, obj); + set_slot(obj, SLOT_DATA, data); + set_slot(obj, SLOT_CFUNC, js_mkfun(fn)); + + ant_value_t func = js_obj_to_func(obj); + GC_ROOT_RESTORE(js, root_mark); + return func; } static ant_value_t mkpromise(ant_t *js) { ant_value_t obj = mkobj(js, 0); if (is_err(obj)) return obj; - - uint32_t pid = next_promise_id++; - set_slot(js, obj, SLOT_PID, tov((double)pid)); + if (!get_promise_data(js, mkval(T_PROMISE, vdata(obj)), true)) + return js_mkerr(js, "out of memory"); ant_value_t promise_ctor = js_get(js, js_glob(js), "Promise"); if (vtype(promise_ctor) == T_FUNC || vtype(promise_ctor) == T_CFUNC) { - set_slot(js, obj, SLOT_CTOR, promise_ctor); + set_slot(obj, SLOT_CTOR, promise_ctor); } ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7); if (is_object_type(promise_proto)) { - set_slot(js, obj, SLOT_PROTO, promise_proto); + js_set_proto_init(obj, promise_proto); } - promise_data_entry_t *pd = get_promise_data(pid, true); - if (pd) pd->obj_offset = (ant_offset_t)vdata(js_as_obj(obj)); return mkval(T_PROMISE, vdata(obj)); } static inline void trigger_handlers(ant_t *js, ant_value_t p) { - uint32_t pid = get_promise_id(js, p); - queue_promise_trigger(pid); + queue_promise_trigger(p); } -void js_process_promise_handlers(ant_t *js, uint32_t pid) { - promise_data_entry_t *pd = get_promise_data(pid, false); +void js_process_promise_handlers(ant_t *js, ant_value_t promise) { + ant_object_t *pobj = js_obj_ptr(promise); + ant_promise_state_t *pd = get_promise_data(js, promise, false); if (!pd) return; int state = pd->state; ant_value_t val = pd->value; unsigned int len = utarray_len(pd->handlers); - if (len == 0) { return; } pd->processing = true; + if (len == 0) { return; } + gc_root_pending_promise(pobj); + pd->processing = true; for (unsigned int i = 0; i < len; i++) { promise_handler_t *h = (promise_handler_t *)utarray_eltptr(pd->handlers, i); @@ -9691,65 +10118,78 @@ void js_process_promise_handlers(ant_t *js, uint32_t pid) { pd->processing = false; utarray_clear(pd->handlers); + gc_unroot_pending_promise(js_obj_ptr(promise)); } static void resolve_promise(ant_t *js, ant_value_t p, ant_value_t val) { - uint32_t pid = get_promise_id(js, p); - promise_data_entry_t *pd = get_promise_data(pid, false); - if (!pd || pd->state != 0) return; + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, p); + GC_ROOT_PIN(js, val); + + ant_promise_state_t *pd = get_promise_data(js, p, false); + if (!pd || pd->state != 0) { + GC_ROOT_RESTORE(js, root_mark); + return; + } if (vtype(val) == T_PROMISE) { - uint32_t val_pid = get_promise_id(js, val); - if (val_pid == pid) { + if (vdata(js_as_obj(val)) == vdata(js_as_obj(p))) { ant_value_t err = js_mkerr(js, "TypeError: Chaining cycle"); - return reject_promise(js, p, err); + GC_ROOT_PIN(js, err); + reject_promise(js, p, err); + GC_ROOT_RESTORE(js, root_mark); + return; } - - ant_value_t res_obj = mkobj(js, 0); - set_slot(js, res_obj, SLOT_DATA, p); - set_slot(js, res_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal)); - ant_value_t res_fn = js_obj_to_func(res_obj); - - ant_value_t rej_obj = mkobj(js, 0); - set_slot(js, rej_obj, SLOT_DATA, p); - set_slot(js, rej_obj, SLOT_CFUNC, js_mkfun(builtin_reject_internal)); - ant_value_t rej_fn = js_obj_to_func(rej_obj); - - ant_value_t call_args[] = { res_fn, rej_fn }; + + ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); + GC_ROOT_PIN(js, res_fn); + if (is_err(res_fn)) { + reject_promise(js, p, res_fn); + GC_ROOT_RESTORE(js, root_mark); + return; + } + + ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); + GC_ROOT_PIN(js, rej_fn); + if (is_err(rej_fn)) { + reject_promise(js, p, rej_fn); + GC_ROOT_RESTORE(js, root_mark); + return; + } + ant_value_t then_prop = js_get(js, val, "then"); - + GC_ROOT_PIN(js, then_prop); + if (vtype(then_prop) == T_FUNC || vtype(then_prop) == T_CFUNC) { - (void)sv_vm_call(js->vm, js, then_prop, val, call_args, 2, NULL, false); return; + ant_value_t call_args[] = { res_fn, rej_fn }; + (void)sv_vm_call(js->vm, js, then_prop, val, call_args, 2, NULL, false); + GC_ROOT_RESTORE(js, root_mark); + return; } } pd->state = 1; pd->value = val; + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val); trigger_handlers(js, p); + GC_ROOT_RESTORE(js, root_mark); } static void reject_promise(ant_t *js, ant_value_t p, ant_value_t val) { - uint32_t pid = get_promise_id(js, p); - promise_data_entry_t *pd = get_promise_data(pid, false); + ant_promise_state_t *pd = get_promise_data(js, p, false); if (!pd || pd->state != 0) return; pd->state = 2; pd->value = val; - - if (!pd->has_rejection_handler) { - promise_data_entry_t *existing = NULL; - HASH_FIND(hh_unhandled, unhandled_rejections, &pd->promise_id, sizeof(uint32_t), existing); - if (!existing) { - HASH_ADD(hh_unhandled, unhandled_rejections, promise_id, sizeof(uint32_t), pd); - } - } + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), val); + pd->unhandled_reported = false; trigger_handlers(js, p); } static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - ant_value_t p = get_slot(js, me, SLOT_DATA); + ant_value_t p = get_slot(me, SLOT_DATA); if (vtype(p) != T_PROMISE) return js_mkundef(); resolve_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); return js_mkundef(); @@ -9757,7 +10197,7 @@ static ant_value_t builtin_resolve_internal(ant_t *js, ant_value_t *args, int na static ant_value_t builtin_reject_internal(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - ant_value_t p = get_slot(js, me, SLOT_DATA); + ant_value_t p = get_slot(me, SLOT_DATA); if (vtype(p) != T_PROMISE) return js_mkundef(); reject_promise(js, p, nargs > 0 ? args[0] : js_mkundef()); return js_mkundef(); @@ -9772,31 +10212,46 @@ static ant_value_t builtin_Promise(ant_t *js, ant_value_t *args, int nargs) { const char *val_str = nargs == 0 ? "undefined" : js_str(js, args[0]); return js_mkerr_typed(js, JS_ERR_TYPE, "Promise resolver %s is not a function", val_str); } - + + GC_ROOT_SAVE(root_mark, js); + ant_value_t executor = args[0]; + GC_ROOT_PIN(js, executor); + ant_value_t p = mkpromise(js); + if (is_err(p)) { + GC_ROOT_RESTORE(js, root_mark); + return p; + } + GC_ROOT_PIN(js, p); + ant_value_t new_target = js->new_target; ant_value_t p_obj = js_as_obj(p); - + ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7); ant_value_t instance_proto = js_instance_proto_from_new_target(js, promise_proto); - - if (vtype(new_target) == T_FUNC || vtype(new_target) == T_CFUNC) set_slot(js, p_obj, SLOT_CTOR, new_target); - if (is_object_type(instance_proto)) set_slot(js, p_obj, SLOT_PROTO, instance_proto); - - ant_value_t res_obj = mkobj(js, 0); - set_slot(js, res_obj, SLOT_DATA, p); - set_slot(js, res_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal)); - - ant_value_t res_fn = js_obj_to_func(res_obj); - ant_value_t rej_obj = mkobj(js, 0); - - set_slot(js, rej_obj, SLOT_DATA, p); - set_slot(js, rej_obj, SLOT_CFUNC, js_mkfun(builtin_reject_internal)); - - ant_value_t rej_fn = js_obj_to_func(rej_obj); + GC_ROOT_PIN(js, instance_proto); + + if (vtype(new_target) == T_FUNC || vtype(new_target) == T_CFUNC) set_slot(p_obj, SLOT_CTOR, new_target); + if (is_object_type(instance_proto)) js_set_proto_init(p_obj, instance_proto); + + ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); + GC_ROOT_PIN(js, res_fn); + if (is_err(res_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return res_fn; + } + + ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); + GC_ROOT_PIN(js, rej_fn); + if (is_err(rej_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return rej_fn; + } + ant_value_t exec_args[] = { res_fn, rej_fn }; - sv_vm_call(js->vm, js, args[0], js_mkundef(), exec_args, 2, NULL, false); - + sv_vm_call(js->vm, js, executor, js_mkundef(), exec_args, 2, NULL, false); + + GC_ROOT_RESTORE(js, root_mark); return p; } @@ -9822,66 +10277,87 @@ static ant_value_t promise_species_noop_executor(ant_t *js, ant_value_t *args, i static ant_value_t builtin_promise_then(ant_t *js, ant_value_t *args, int nargs) { ant_value_t p = js->this_val; if (vtype(p) != T_PROMISE) return js_mkerr(js, "not a promise"); - + + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, p); + ant_value_t promise_ctor = js_get(js, js_glob(js), "Promise"); + GC_ROOT_PIN(js, promise_ctor); ant_value_t species_ctor = promise_ctor; + GC_ROOT_PIN(js, species_ctor); ant_value_t p_obj = js_as_obj(p); ant_value_t ctor = js_get(js, p_obj, "constructor"); - - if (is_err(ctor)) return ctor; - if (vtype(ctor) == T_UNDEF) ctor = get_slot(js, p_obj, SLOT_CTOR); - + GC_ROOT_PIN(js, ctor); + + if (is_err(ctor)) { + GC_ROOT_RESTORE(js, root_mark); + return ctor; + } + if (vtype(ctor) == T_UNDEF) ctor = get_slot(p_obj, SLOT_CTOR); + ant_value_t species = get_ctor_species_value(js, ctor); - if (is_err(species)) return species; - + GC_ROOT_PIN(js, species); + if (is_err(species)) { + GC_ROOT_RESTORE(js, root_mark); + return species; + } + if (vtype(species) == T_FUNC || vtype(species) == T_CFUNC) { species_ctor = species; } else if (vtype(species) == T_NULL) { species_ctor = promise_ctor; } else if (vtype(species) != T_UNDEF) { - return js_mkerr_typed(js, JS_ERR_TYPE, "Promise species is not a constructor"); + ant_value_t err = js_mkerr_typed(js, JS_ERR_TYPE, "Promise species is not a constructor"); + GC_ROOT_RESTORE(js, root_mark); + return err; } - + ant_value_t nextP = mkpromise(js); + GC_ROOT_PIN(js, nextP); if ((vtype(species_ctor) == T_FUNC || vtype(species_ctor) == T_CFUNC) && !(vtype(species_ctor) == vtype(promise_ctor) && vdata(species_ctor) == vdata(promise_ctor))) { ant_value_t species_proto = js_get(js, species_ctor, "prototype"); if (is_object_type(species_proto)) - set_proto(js, js_as_obj(nextP), species_proto); - set_slot(js, js_as_obj(nextP), SLOT_CTOR, species_ctor); + js_set_proto_init(js_as_obj(nextP), species_proto); + set_slot(js_as_obj(nextP), SLOT_CTOR, species_ctor); } else { - ant_value_t p_proto = get_slot(js, js_as_obj(p), SLOT_PROTO); + ant_value_t p_proto = get_slot(js_as_obj(p), SLOT_PROTO); if (vtype(p_proto) == T_OBJ) { - set_slot(js, js_as_obj(nextP), SLOT_PROTO, p_proto); - ant_value_t p_ctor = get_slot(js, js_as_obj(p), SLOT_CTOR); - if (vtype(p_ctor) == T_FUNC) set_slot(js, js_as_obj(nextP), SLOT_CTOR, p_ctor); + js_set_proto_init(js_as_obj(nextP), p_proto); + ant_value_t p_ctor = get_slot(js_as_obj(p), SLOT_CTOR); + if (vtype(p_ctor) == T_FUNC) set_slot(js_as_obj(nextP), SLOT_CTOR, p_ctor); } } - + ant_value_t onFulfilled = nargs > 0 ? args[0] : js_mkundef(); ant_value_t onRejected = nargs > 1 ? args[1] : js_mkundef(); - - uint32_t pid = get_promise_id(js, p); - uint32_t next_pid = get_promise_id(js, nextP); - - promise_data_entry_t *next_pd = get_promise_data(next_pid, false); - if (next_pd) next_pd->trigger_pid = pid; - - promise_data_entry_t *pd = get_promise_data(pid, false); + GC_ROOT_PIN(js, onFulfilled); + GC_ROOT_PIN(js, onRejected); + + ant_promise_state_t *next_pd = get_promise_data(js, nextP, false); + if (next_pd) next_pd->trigger_parent = p; + + ant_promise_state_t *pd = get_promise_data(js, p, false); if (pd) { promise_handler_t h = { onFulfilled, onRejected, nextP }; utarray_push_back(pd->handlers, &h); - + + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), nextP); + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), onFulfilled); + gc_write_barrier(js, js_obj_ptr(js_as_obj(p)), onRejected); + if (vtype(onRejected) == T_FUNC || vtype(onRejected) == T_CFUNC) { pd->has_rejection_handler = true; - promise_data_entry_t *in_unhandled = NULL; - HASH_FIND(hh_unhandled, unhandled_rejections, &pd->promise_id, sizeof(uint32_t), in_unhandled); - if (in_unhandled) HASH_DELETE(hh_unhandled, unhandled_rejections, pd); + pd->unhandled_reported = false; } + + if (pd->state == 0) + gc_root_pending_promise(js_obj_ptr(p)); } - + if (pd && pd->state != 0) trigger_handlers(js, p); + GC_ROOT_RESTORE(js, root_mark); return nextP; } @@ -9892,12 +10368,12 @@ static ant_value_t builtin_promise_catch(ant_t *js, ant_value_t *args, int nargs static ant_value_t finally_value_thunk(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - return get_slot(js, me, SLOT_DATA); + return get_slot(me, SLOT_DATA); } static ant_value_t finally_thrower(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - ant_value_t reason = get_slot(js, me, SLOT_DATA); + ant_value_t reason = get_slot(me, SLOT_DATA); ant_value_t rejected = js_mkpromise(js); js_reject_promise(js, rejected, reason); return rejected; @@ -9911,77 +10387,109 @@ static ant_value_t finally_identity_reject(ant_t *js, ant_value_t *args, int nar } static ant_value_t finally_fulfilled_wrapper(ant_t *js, ant_value_t *args, int nargs) { + GC_ROOT_SAVE(root_mark, js); ant_value_t me = js->current_func; - ant_value_t callback = get_slot(js, me, SLOT_DATA); + ant_value_t callback = get_slot(me, SLOT_DATA); ant_value_t value = nargs > 0 ? args[0] : js_mkundef(); - + GC_ROOT_PIN(js, callback); + GC_ROOT_PIN(js, value); + ant_value_t result = js_mkundef(); if (vtype(callback) == T_FUNC || vtype(callback) == T_CFUNC) { result = sv_vm_call(js->vm, js, callback, js_mkundef(), NULL, 0, NULL, false); - if (is_err(result)) return result; + if (is_err(result)) { + GC_ROOT_RESTORE(js, root_mark); + return result; + } } - + GC_ROOT_PIN(js, result); + if (vtype(result) == T_PROMISE || (vtype(result) == T_OBJ && vtype(js_get(js, result, "then")) == T_FUNC)) { - ant_value_t thunk_obj = mkobj(js, 0); - set_slot(js, thunk_obj, SLOT_DATA, value); - set_slot(js, thunk_obj, SLOT_CFUNC, js_mkfun(finally_value_thunk)); - ant_value_t thunk_fn = js_obj_to_func(thunk_obj); - + ant_value_t thunk_fn = make_data_cfunc(js, value, finally_value_thunk); + GC_ROOT_PIN(js, thunk_fn); + if (is_err(thunk_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return thunk_fn; + } + ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); - ant_value_t then_fn = js_get(js, result, "then"); + GC_ROOT_PIN(js, then_fn); ant_value_t call_args[] = { thunk_fn, identity_rej_fn }; - return sv_vm_call(js->vm, js, then_fn, result, call_args, 2, NULL, false); + ant_value_t ret = sv_vm_call(js->vm, js, then_fn, result, call_args, 2, NULL, false); + GC_ROOT_RESTORE(js, root_mark); + return ret; } - + + GC_ROOT_RESTORE(js, root_mark); return value; } static ant_value_t finally_rejected_wrapper(ant_t *js, ant_value_t *args, int nargs) { + GC_ROOT_SAVE(root_mark, js); ant_value_t me = js->current_func; - ant_value_t callback = get_slot(js, me, SLOT_DATA); + ant_value_t callback = get_slot(me, SLOT_DATA); ant_value_t reason = nargs > 0 ? args[0] : js_mkundef(); - + GC_ROOT_PIN(js, callback); + GC_ROOT_PIN(js, reason); + ant_value_t result = js_mkundef(); if (vtype(callback) == T_FUNC || vtype(callback) == T_CFUNC) { result = sv_vm_call(js->vm, js, callback, js_mkundef(), NULL, 0, NULL, false); - if (is_err(result)) return result; + if (is_err(result)) { + GC_ROOT_RESTORE(js, root_mark); + return result; + } } - + GC_ROOT_PIN(js, result); + if (vtype(result) == T_PROMISE || (vtype(result) == T_OBJ && vtype(js_get(js, result, "then")) == T_FUNC)) { - ant_value_t thrower_obj = mkobj(js, 0); - set_slot(js, thrower_obj, SLOT_DATA, reason); - set_slot(js, thrower_obj, SLOT_CFUNC, js_mkfun(finally_thrower)); - - ant_value_t thrower_fn = js_obj_to_func(thrower_obj); + ant_value_t thrower_fn = make_data_cfunc(js, reason, finally_thrower); + GC_ROOT_PIN(js, thrower_fn); + if (is_err(thrower_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return thrower_fn; + } ant_value_t identity_rej_fn = js_mkfun(finally_identity_reject); - ant_value_t then_prop = js_get(js, result, "then"); + GC_ROOT_PIN(js, then_prop); ant_value_t call_args[] = { thrower_fn, identity_rej_fn }; - - return sv_vm_call(js->vm, js, then_prop, result, call_args, 2, NULL, false); + + ant_value_t ret = sv_vm_call(js->vm, js, then_prop, result, call_args, 2, NULL, false); + GC_ROOT_RESTORE(js, root_mark); + return ret; } - + ant_value_t rejected = js_mkpromise(js); + GC_ROOT_PIN(js, rejected); js_reject_promise(js, rejected, reason); + GC_ROOT_RESTORE(js, root_mark); return rejected; } static ant_value_t builtin_promise_finally(ant_t *js, ant_value_t *args, int nargs) { + GC_ROOT_SAVE(root_mark, js); ant_value_t callback = nargs > 0 ? args[0] : js_mkundef(); - - ant_value_t fulfilled_obj = mkobj(js, 0); - set_slot(js, fulfilled_obj, SLOT_DATA, callback); - set_slot(js, fulfilled_obj, SLOT_CFUNC, js_mkfun(finally_fulfilled_wrapper)); - ant_value_t fulfilled_fn = js_obj_to_func(fulfilled_obj); - - ant_value_t rejected_obj = mkobj(js, 0); - set_slot(js, rejected_obj, SLOT_DATA, callback); - set_slot(js, rejected_obj, SLOT_CFUNC, js_mkfun(finally_rejected_wrapper)); - ant_value_t rejected_fn = js_obj_to_func(rejected_obj); - + GC_ROOT_PIN(js, callback); + + ant_value_t fulfilled_fn = make_data_cfunc(js, callback, finally_fulfilled_wrapper); + GC_ROOT_PIN(js, fulfilled_fn); + if (is_err(fulfilled_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return fulfilled_fn; + } + + ant_value_t rejected_fn = make_data_cfunc(js, callback, finally_rejected_wrapper); + GC_ROOT_PIN(js, rejected_fn); + if (is_err(rejected_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return rejected_fn; + } + ant_value_t args_then[] = { fulfilled_fn, rejected_fn }; - return builtin_promise_then(js, args_then, 2); + ant_value_t ret = builtin_promise_then(js, args_then, 2); + GC_ROOT_RESTORE(js, root_mark); + return ret; } static ant_value_t builtin_Promise_try(ant_t *js, ant_value_t *args, int nargs) { @@ -10004,37 +10512,55 @@ static ant_value_t builtin_Promise_try(ant_t *js, ant_value_t *args, int nargs) } static ant_value_t builtin_Promise_withResolvers(ant_t *js, ant_value_t *args, int nargs) { + GC_ROOT_SAVE(root_mark, js); ant_value_t p = mkpromise(js); - - ant_value_t res_obj = mkobj(js, 0); - set_slot(js, res_obj, SLOT_DATA, p); - set_slot(js, res_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal)); - ant_value_t res_fn = js_obj_to_func(res_obj); - - ant_value_t rej_obj = mkobj(js, 0); - set_slot(js, rej_obj, SLOT_DATA, p); - set_slot(js, rej_obj, SLOT_CFUNC, js_mkfun(builtin_reject_internal)); - ant_value_t rej_fn = js_obj_to_func(rej_obj); - + GC_ROOT_PIN(js, p); + + ant_value_t res_fn = make_data_cfunc(js, p, builtin_resolve_internal); + GC_ROOT_PIN(js, res_fn); + if (is_err(res_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return res_fn; + } + + ant_value_t rej_fn = make_data_cfunc(js, p, builtin_reject_internal); + GC_ROOT_PIN(js, rej_fn); + if (is_err(rej_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return rej_fn; + } + ant_value_t result = js_newobj(js); + GC_ROOT_PIN(js, result); js_setprop(js, result, js_mkstr(js, "promise", 7), p); js_setprop(js, result, js_mkstr(js, "resolve", 7), res_fn); js_setprop(js, result, js_mkstr(js, "reject", 6), rej_fn); - + + GC_ROOT_RESTORE(js, root_mark); return result; } static ant_value_t mkpromise_with_ctor(ant_t *js, ant_value_t ctor) { + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, ctor); ant_value_t p = mkpromise(js); - if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) return p; + GC_ROOT_PIN(js, p); + if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) { + GC_ROOT_RESTORE(js, root_mark); + return p; + } ant_value_t proto = js_get(js, ctor, "prototype"); - if (is_err(proto)) return proto; + if (is_err(proto)) { + GC_ROOT_RESTORE(js, root_mark); + return proto; + } if (is_object_type(proto)) { ant_value_t p_obj = js_as_obj(p); - set_slot(js, p_obj, SLOT_CTOR, ctor); - set_slot(js, p_obj, SLOT_PROTO, proto); + set_slot(p_obj, SLOT_CTOR, ctor); + js_set_proto_init(p_obj, proto); } + GC_ROOT_RESTORE(js, root_mark); return p; } @@ -10054,7 +10580,7 @@ static ant_value_t builtin_Promise_all_resolve_handler(ant_t *js, ant_value_t *a js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)remaining)); if (remaining == 0) { - ant_value_t result_promise = get_slot(js, tracker, SLOT_DATA); + ant_value_t result_promise = get_slot(tracker, SLOT_DATA); resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); } @@ -10064,7 +10590,7 @@ static ant_value_t builtin_Promise_all_resolve_handler(ant_t *js, ant_value_t *a static ant_value_t builtin_Promise_all_reject_handler(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; ant_value_t tracker = js_get(js, me, "tracker"); - ant_value_t result_promise = get_slot(js, tracker, SLOT_DATA); + ant_value_t result_promise = get_slot(tracker, SLOT_DATA); ant_value_t reason = nargs > 0 ? args[0] : js_mkundef(); reject_promise(js, result_promise, reason); @@ -10078,75 +10604,117 @@ typedef struct { } promise_all_iter_ctx_t; static iter_action_t promise_all_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) { + GC_ROOT_SAVE(root_mark, js); promise_all_iter_ctx_t *pctx = (promise_all_iter_ctx_t *)ctx; ant_value_t item = value; - + GC_ROOT_PIN(js, item); + if (vtype(item) != T_PROMISE) { ant_value_t wrap_args[] = { item }; item = builtin_Promise_resolve(js, wrap_args, 1); + GC_ROOT_PIN(js, item); } - + ant_value_t resolve_obj = mkobj(js, 0); - set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_resolve_handler)); + if (is_err(resolve_obj)) { + *out = resolve_obj; + GC_ROOT_RESTORE(js, root_mark); + return ITER_ERROR; + } + GC_ROOT_PIN(js, resolve_obj); + set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_resolve_handler)); js_setprop(js, resolve_obj, js_mkstr(js, "index", 5), tov((double)pctx->index)); js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), pctx->tracker); ant_value_t resolve_fn = js_obj_to_func(resolve_obj); - + GC_ROOT_PIN(js, resolve_fn); + ant_value_t reject_obj = mkobj(js, 0); - set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_reject_handler)); + if (is_err(reject_obj)) { + *out = reject_obj; + GC_ROOT_RESTORE(js, root_mark); + return ITER_ERROR; + } + GC_ROOT_PIN(js, reject_obj); + set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_all_reject_handler)); js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), pctx->tracker); ant_value_t reject_fn = js_obj_to_func(reject_obj); - + GC_ROOT_PIN(js, reject_fn); + ant_value_t then_args[] = { resolve_fn, reject_fn }; ant_value_t saved_this = js->this_val; + GC_ROOT_PIN(js, saved_this); js->this_val = item; - builtin_promise_then(js, then_args, 2); + ant_value_t then_result = builtin_promise_then(js, then_args, 2); js->this_val = saved_this; - + if (is_err(then_result)) { + *out = then_result; + GC_ROOT_RESTORE(js, root_mark); + return ITER_ERROR; + } + pctx->index++; + GC_ROOT_RESTORE(js, root_mark); return ITER_CONTINUE; } static ant_value_t builtin_Promise_all(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Promise.all requires an iterable"); - + + GC_ROOT_SAVE(root_mark, js); ant_value_t iterable = args[0]; + GC_ROOT_PIN(js, iterable); uint8_t t = vtype(iterable); - if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.all requires an iterable"); - + if (t != T_ARR && t != T_OBJ) { + ant_value_t err = js_mkerr(js, "Promise.all requires an iterable"); + GC_ROOT_RESTORE(js, root_mark); + return err; + } + ant_value_t ctor = js->this_val; + GC_ROOT_PIN(js, ctor); if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef(); - + ant_value_t result_promise = mkpromise_with_ctor(js, ctor); - if (is_err(result_promise)) return result_promise; - + GC_ROOT_PIN(js, result_promise); + if (is_err(result_promise)) { + GC_ROOT_RESTORE(js, root_mark); + return result_promise; + } + ant_value_t tracker = mkobj(js, 0); + GC_ROOT_PIN(js, tracker); ant_value_t results = mkarr(js); - + GC_ROOT_PIN(js, results); + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov(0.0)); js_setprop(js, tracker, js_mkstr(js, "results", 7), results); - set_slot(js, tracker, SLOT_DATA, result_promise); - + set_slot(tracker, SLOT_DATA, result_promise); + promise_all_iter_ctx_t ctx = { .tracker = tracker, .index = 0 }; ant_value_t iter_result = iter_foreach(js, iterable, promise_all_iter_cb, &ctx); - - if (is_err(iter_result)) return iter_result; - + + if (is_err(iter_result)) { + GC_ROOT_RESTORE(js, root_mark); + return iter_result; + } + int len = ctx.index; { - ant_offset_t doff = get_dense_buf(js, results); + ant_offset_t doff = get_dense_buf(results); if (doff) { - if ((ant_offset_t)len > dense_capacity(js, doff)) doff = dense_grow(js, results, (ant_offset_t)len); - if (doff) dense_set_length(js, doff, (ant_offset_t)len); + if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, results, (ant_offset_t)len); + if (doff) array_len_set(js, results, (ant_offset_t)len); } } - + if (len == 0) { resolve_promise(js, result_promise, mkval(T_ARR, vdata(results))); + GC_ROOT_RESTORE(js, root_mark); return result_promise; } - + js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); + GC_ROOT_RESTORE(js, root_mark); return result_promise; } @@ -10158,95 +10726,130 @@ typedef struct { } promise_race_iter_ctx_t; static iter_action_t promise_race_iter_cb(ant_t *js, ant_value_t value, void *ctx, ant_value_t *out) { + GC_ROOT_SAVE(root_mark, js); promise_race_iter_ctx_t *pctx = (promise_race_iter_ctx_t *)ctx; ant_value_t item = value; - + GC_ROOT_PIN(js, item); + if (vtype(item) != T_PROMISE) { resolve_promise(js, pctx->result_promise, item); pctx->settled = true; + GC_ROOT_RESTORE(js, root_mark); return ITER_BREAK; } - - uint32_t item_pid = get_promise_id(js, item); - promise_data_entry_t *pd = get_promise_data(item_pid, false); + + ant_promise_state_t *pd = get_promise_data(js, item, false); if (pd) { if (pd->state == 1) { resolve_promise(js, pctx->result_promise, pd->value); pctx->settled = true; + GC_ROOT_RESTORE(js, root_mark); return ITER_BREAK; } else if (pd->state == 2) { reject_promise(js, pctx->result_promise, pd->value); pctx->settled = true; + GC_ROOT_RESTORE(js, root_mark); return ITER_BREAK; } } - + ant_value_t then_args[] = { pctx->resolve_fn, pctx->reject_fn }; ant_value_t saved_this = js->this_val; + GC_ROOT_PIN(js, saved_this); js->this_val = item; - builtin_promise_then(js, then_args, 2); + ant_value_t then_result = builtin_promise_then(js, then_args, 2); js->this_val = saved_this; - + if (is_err(then_result)) { + *out = then_result; + GC_ROOT_RESTORE(js, root_mark); + return ITER_ERROR; + } + + GC_ROOT_RESTORE(js, root_mark); return ITER_CONTINUE; } static ant_value_t builtin_Promise_race(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Promise.race requires an iterable"); - + + GC_ROOT_SAVE(root_mark, js); ant_value_t iterable = args[0]; + GC_ROOT_PIN(js, iterable); uint8_t t = vtype(iterable); - if (t != T_ARR && t != T_OBJ) return js_mkerr(js, "Promise.race requires an iterable"); - + if (t != T_ARR && t != T_OBJ) { + ant_value_t err = js_mkerr(js, "Promise.race requires an iterable"); + GC_ROOT_RESTORE(js, root_mark); + return err; + } + ant_value_t ctor = js->this_val; + GC_ROOT_PIN(js, ctor); if (vtype(ctor) != T_FUNC && vtype(ctor) != T_CFUNC) ctor = js_mkundef(); ant_value_t result_promise = mkpromise_with_ctor(js, ctor); - if (is_err(result_promise)) return result_promise; - - ant_value_t resolve_obj = mkobj(js, 0); - set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_resolve_internal)); - set_slot(js, resolve_obj, SLOT_DATA, result_promise); - ant_value_t resolve_fn = js_obj_to_func(resolve_obj); - - ant_value_t reject_obj = mkobj(js, 0); - set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(builtin_reject_internal)); - set_slot(js, reject_obj, SLOT_DATA, result_promise); - ant_value_t reject_fn = js_obj_to_func(reject_obj); - + GC_ROOT_PIN(js, result_promise); + if (is_err(result_promise)) { + GC_ROOT_RESTORE(js, root_mark); + return result_promise; + } + + ant_value_t resolve_fn = make_data_cfunc(js, result_promise, builtin_resolve_internal); + GC_ROOT_PIN(js, resolve_fn); + if (is_err(resolve_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return resolve_fn; + } + + ant_value_t reject_fn = make_data_cfunc(js, result_promise, builtin_reject_internal); + GC_ROOT_PIN(js, reject_fn); + if (is_err(reject_fn)) { + GC_ROOT_RESTORE(js, root_mark); + return reject_fn; + } + promise_race_iter_ctx_t ctx = { .result_promise = result_promise, .resolve_fn = resolve_fn, .reject_fn = reject_fn, .settled = false }; - + ant_value_t iter_result = iter_foreach(js, iterable, promise_race_iter_cb, &ctx); - if (is_err(iter_result)) return iter_result; - + if (is_err(iter_result)) { + GC_ROOT_RESTORE(js, root_mark); + return iter_result; + } + + GC_ROOT_RESTORE(js, root_mark); return result_promise; } static ant_value_t mk_aggregate_error(ant_t *js, ant_value_t errors) { + GC_ROOT_SAVE(root_mark, js); + GC_ROOT_PIN(js, errors); ant_value_t args[] = { errors, js_mkstr(js, "All promises were rejected", 26) }; ant_offset_t off = lkp(js, js_glob(js), "AggregateError", 14); - ant_value_t ctor = off ? resolveprop(js, mkval(T_PROP, off)) : js_mkundef(); - return sv_vm_call(js->vm, js, ctor, js_mkundef(), args, 2, NULL, false); + ant_value_t ctor = off ? propref_load(js, off) : js_mkundef(); + GC_ROOT_PIN(js, ctor); + ant_value_t ret = sv_vm_call(js->vm, js, ctor, js_mkundef(), args, 2, NULL, false); + GC_ROOT_RESTORE(js, root_mark); + return ret; } static bool promise_any_try_resolve(ant_t *js, ant_value_t tracker, ant_value_t value) { if (js_truthy(js, js_get(js, tracker, "resolved"))) return false; js_set(js, tracker, "resolved", js_true); - resolve_promise(js, get_slot(js, tracker, SLOT_DATA), value); + resolve_promise(js, get_slot(tracker, SLOT_DATA), value); return true; } static void promise_any_record_rejection(ant_t *js, ant_value_t tracker, int index, ant_value_t reason) { - ant_value_t errors = resolveprop(js, js_get(js, tracker, "errors")); + ant_value_t errors = js_get(js, tracker, "errors"); arr_set(js, errors, (ant_offset_t)index, reason); int remaining = (int)tod(js_get(js, tracker, "remaining")) - 1; js_set(js, tracker, "remaining", tov((double)remaining)); - if (remaining == 0) reject_promise(js, get_slot(js, tracker, SLOT_DATA), mk_aggregate_error(js, errors)); + if (remaining == 0) reject_promise(js, get_slot(tracker, SLOT_DATA), mk_aggregate_error(js, errors)); } static ant_value_t builtin_Promise_any_resolve_handler(ant_t *js, ant_value_t *args, int nargs) { @@ -10266,77 +10869,106 @@ static ant_value_t builtin_Promise_any_reject_handler(ant_t *js, ant_value_t *ar static ant_value_t builtin_Promise_any(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Promise.any requires an array"); - + + GC_ROOT_SAVE(root_mark, js); ant_value_t arr = args[0]; - if (vtype(arr) != T_ARR) return js_mkerr(js, "Promise.any requires an array"); - + GC_ROOT_PIN(js, arr); + if (vtype(arr) != T_ARR) { + ant_value_t err = js_mkerr(js, "Promise.any requires an array"); + GC_ROOT_RESTORE(js, root_mark); + return err; + } + int len = (int)get_array_length(js, arr); - + if (len == 0) { ant_value_t reject_args[] = { mk_aggregate_error(js, mkarr(js)) }; - return builtin_Promise_reject(js, reject_args, 1); + ant_value_t ret = builtin_Promise_reject(js, reject_args, 1); + GC_ROOT_RESTORE(js, root_mark); + return ret; } - + ant_value_t result_promise = mkpromise(js); + GC_ROOT_PIN(js, result_promise); ant_value_t tracker = mkobj(js, 0); + GC_ROOT_PIN(js, tracker); ant_value_t errors = mkarr(js); - - set_slot(js, tracker, SLOT_DATA, result_promise); + GC_ROOT_PIN(js, errors); + + set_slot(tracker, SLOT_DATA, result_promise); js_setprop(js, tracker, js_mkstr(js, "remaining", 9), tov((double)len)); js_setprop(js, tracker, js_mkstr(js, "errors", 6), errors); js_setprop(js, tracker, js_mkstr(js, "resolved", 8), js_false); { - ant_offset_t doff = get_dense_buf(js, errors); + ant_offset_t doff = get_dense_buf(errors); if (doff) { - if ((ant_offset_t)len > dense_capacity(js, doff)) doff = dense_grow(js, errors, (ant_offset_t)len); - if (doff) dense_set_length(js, doff, (ant_offset_t)len); + if ((ant_offset_t)len > dense_capacity(doff)) doff = dense_grow(js, errors, (ant_offset_t)len); + if (doff) array_len_set(js, errors, (ant_offset_t)len); } } - + for (int i = 0; i < len; i++) { ant_value_t item = arr_get(js, arr, (ant_offset_t)i); - item = resolveprop(js, item); - + GC_ROOT_PIN(js, item); if (vtype(item) != T_PROMISE) { promise_any_try_resolve(js, tracker, item); + GC_ROOT_RESTORE(js, root_mark); return result_promise; } - - uint32_t item_pid = get_promise_id(js, item); - promise_data_entry_t *pd = get_promise_data(item_pid, false); + + ant_promise_state_t *pd = get_promise_data(js, item, false); if (pd) { pd->has_rejection_handler = true; - promise_data_entry_t *in_unhandled = NULL; - HASH_FIND(hh_unhandled, unhandled_rejections, &pd->promise_id, sizeof(uint32_t), in_unhandled); - if (in_unhandled) HASH_DELETE(hh_unhandled, unhandled_rejections, pd); + pd->unhandled_reported = false; if (pd->state == 1) { promise_any_try_resolve(js, tracker, pd->value); + GC_ROOT_RESTORE(js, root_mark); return result_promise; } else if (pd->state == 2) { promise_any_record_rejection(js, tracker, i, pd->value); continue; } } - + ant_value_t resolve_obj = mkobj(js, 0); - set_slot(js, resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_resolve_handler)); + if (is_err(resolve_obj)) { + GC_ROOT_RESTORE(js, root_mark); + return resolve_obj; + } + GC_ROOT_PIN(js, resolve_obj); + set_slot(resolve_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_resolve_handler)); js_setprop(js, resolve_obj, js_mkstr(js, "tracker", 7), tracker); - + ant_value_t reject_obj = mkobj(js, 0); - set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_reject_handler)); + if (is_err(reject_obj)) { + GC_ROOT_RESTORE(js, root_mark); + return reject_obj; + } + GC_ROOT_PIN(js, reject_obj); + set_slot(reject_obj, SLOT_CFUNC, js_mkfun(builtin_Promise_any_reject_handler)); js_setprop(js, reject_obj, js_mkstr(js, "index", 5), tov((double)i)); js_setprop(js, reject_obj, js_mkstr(js, "tracker", 7), tracker); - - ant_value_t then_args[] = { js_obj_to_func(resolve_obj), js_obj_to_func(reject_obj) }; + + ant_value_t resolve_fn = js_obj_to_func(resolve_obj); + GC_ROOT_PIN(js, resolve_fn); + ant_value_t reject_fn = js_obj_to_func(reject_obj); + GC_ROOT_PIN(js, reject_fn); + ant_value_t then_args[] = { resolve_fn, reject_fn }; ant_value_t saved_this = js->this_val; + GC_ROOT_PIN(js, saved_this); js->this_val = item; - builtin_promise_then(js, then_args, 2); + ant_value_t then_result = builtin_promise_then(js, then_args, 2); js->this_val = saved_this; + if (is_err(then_result)) { + GC_ROOT_RESTORE(js, root_mark); + return then_result; + } } - + + GC_ROOT_RESTORE(js, root_mark); return result_promise; } @@ -10375,14 +11007,7 @@ static ant_value_t handle_proxy_instanceof(ant_t *js, ant_value_t l, ant_value_t } ant_value_t current = get_proto(js, l); - for (int depth = 0; vtype(current) != T_NULL && depth < 32; depth++) { - if (vdata(current) == vdata(proto_val)) { - return mkval(T_BOOL, 1); - } - current = get_proto(js, current); - } - - return mkval(T_BOOL, 0); + return mkval(T_BOOL, proto_chain_contains_cycle_safe(js, current, proto_val) ? 1 : 0); } static ant_value_t handle_cfunc_instanceof(ant_value_t l, ant_value_t r, uint8_t ltype) { @@ -10399,16 +11024,45 @@ static ant_value_t handle_cfunc_instanceof(ant_value_t l, ant_value_t r, uint8_t return mkval(T_BOOL, 0); } +static bool proto_chain_contains_cycle_safe(ant_t *js, ant_value_t start, ant_value_t target) { + if (!is_object_type(start) || !is_object_type(target)) return false; + + bool found = false; + ant_value_t slow = start; + ant_value_t fast = start; + + while (is_object_type(slow)) { + if (same_object_identity(slow, target)) { + found = true; + break; + } + slow = get_proto(js, slow); + + if (is_object_type(fast)) fast = get_proto(js, fast); + if (is_object_type(fast)) fast = get_proto(js, fast); + + if ( + is_object_type(slow) && + is_object_type(fast) && + same_object_identity(slow, fast) + ) break; + } + + return found; +} + static ant_value_t walk_prototype_chain(ant_t *js, ant_value_t l, ant_value_t ctor_proto) { ant_value_t current = get_proto(js, l); - const int MAX_DEPTH = 32; - - for (int depth = 0; vtype(current) != T_NULL && depth < MAX_DEPTH; depth++) { - if (vdata(current) == vdata(ctor_proto)) return mkval(T_BOOL, 1); - current = get_proto(js, current); - } - - return mkval(T_BOOL, 0); + return mkval(T_BOOL, proto_chain_contains_cycle_safe(js, current, ctor_proto) ? 1 : 0); +} + +static inline ant_object_t *cached_function_proto_obj(ant_t *js) { + static ant_object_t *cached = NULL; + if (cached) return cached; + ant_value_t proto = get_ctor_proto(js, "Function", 8); + if (!is_object_type(proto)) return NULL; + cached = js_obj_ptr(js_as_obj(proto)); + return cached; } ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r) { @@ -10416,15 +11070,48 @@ ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r) { uint8_t rtype = vtype(r); if (rtype != T_FUNC && rtype != T_CFUNC) { - if (is_proxy(js, r)) return handle_proxy_instanceof(js, l, r, ltype); + if (is_proxy(r)) return handle_proxy_instanceof(js, l, r, ltype); return js_mkerr_typed(js, JS_ERR_TYPE, "Right-hand side of 'instanceof' is not callable"); } if (rtype == T_CFUNC) { return handle_cfunc_instanceof(l, r, ltype); } - - { + + ant_value_t func_obj = js_func_obj(r); + ant_offset_t has_instance_sym_off = (ant_offset_t)vdata(get_hasInstance_sym()); + bool use_slow_has_instance = false; + ant_offset_t own_has_instance = lkp_sym(js, func_obj, has_instance_sym_off); + if (own_has_instance != 0) { + const ant_shape_prop_t *prop_meta = prop_shape_meta(js, own_has_instance); + if (prop_meta && (prop_meta->has_getter || prop_meta->has_setter)) { + use_slow_has_instance = true; + } else { + ant_value_t has_instance = propref_load(js, own_has_instance); + uint8_t hit = vtype(has_instance); + if (hit == T_FUNC || hit == T_CFUNC) { + ant_value_t args[1] = { l }; + ant_value_t result = sv_vm_call(js->vm, js, has_instance, r, args, 1, NULL, false); + if (is_err(result)) return result; + return js_bool(js_truthy(js, result)); + } + if (hit != T_UNDEF && hit != T_NULL) { + return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable"); + } + } + } else { + ant_object_t *func_proto_ptr = cached_function_proto_obj(js); + if (func_proto_ptr && func_proto_ptr->shape && + ant_shape_lookup_symbol(func_proto_ptr->shape, has_instance_sym_off) >= 0) { + use_slow_has_instance = true; + } else if (func_proto_ptr && func_proto_ptr->is_exotic) { + ant_value_t func_proto_obj = mkval(T_OBJ, (uintptr_t)func_proto_ptr); + if (lookup_sym_descriptor(func_proto_obj, has_instance_sym_off)) + use_slow_has_instance = true; + } + } + + if (use_slow_has_instance) { ant_value_t has_instance = js_get_sym(js, r, get_hasInstance_sym()); if (is_err(has_instance)) return has_instance; uint8_t hit = vtype(has_instance); @@ -10438,12 +11125,11 @@ ant_value_t do_instanceof(ant_t *js, ant_value_t l, ant_value_t r) { return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol.hasInstance is not callable"); } } - - ant_value_t func_obj = js_func_obj(r); + ant_offset_t proto_off = lkp_interned(js, func_obj, INTERN_PROTOTYPE, 9); if (proto_off == 0) return mkval(T_BOOL, 0); - ant_value_t ctor_proto = resolveprop(js, mkval(T_PROP, proto_off)); + ant_value_t ctor_proto = propref_load(js, proto_off); uint8_t pt = vtype(ctor_proto); if (pt != T_OBJ && pt != T_ARR && pt != T_FUNC) return mkval(T_BOOL, 0); @@ -10480,7 +11166,7 @@ ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r) { ant_value_t key_str = js_tostring_val(js, key); if (is_err(key_str)) return key_str; ant_offset_t prop_off = vstr(js, key_str, &prop_len); - prop_name = (char *)&js->mem[prop_off]; + prop_name = (char *)(uintptr_t)(prop_off); } if (!is_object_type(r)) { @@ -10488,7 +11174,7 @@ ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r) { return js_mkerr_typed(js, JS_ERR_TYPE, "Cannot use 'in' operator to search for '%.*s' in non-object", (int)prop_len, prop_name); } - if (is_proxy(js, r)) { + if (is_proxy(r)) { ant_value_t result = is_sym ? proxy_has_val(js, r, key) : proxy_has(js, r, prop_name, prop_len); if (is_err(result)) return result; return js_bool(js_truthy(js, result)); @@ -10498,7 +11184,7 @@ ant_value_t do_in(ant_t *js, ant_value_t l, ant_value_t r) { unsigned long idx; ant_offset_t arr_len = get_array_length(js, r); if (parse_array_index(prop_name, prop_len, arr_len, &idx)) return mkval(T_BOOL, arr_has(js, r, (ant_offset_t)idx) ? 1 : 0); - if (prop_len == 6 && memcmp(prop_name, "length", 6) == 0) return mkval(T_BOOL, 1); + if (is_length_key(prop_name, prop_len)) return mkval(T_BOOL, 1); } ant_offset_t found = is_sym ? lkp_sym_proto(js, r, (ant_offset_t)vdata(key)) : lkp_proto(js, r, prop_name, prop_len); @@ -10519,7 +11205,7 @@ static ant_value_t js_get_import_meta_prop(ant_t *js) { ant_offset_t import_off = lkp(js, glob, "import", 6); if (import_off == 0) return js_mkundef(); - ant_value_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); + ant_value_t import_fn = propref_load(js, import_off); if (vtype(import_fn) != T_FUNC) return js_mkundef(); return js_get(js, js_func_obj(import_fn), "meta"); } @@ -10529,7 +11215,7 @@ static void js_set_import_meta_prop(ant_t *js, ant_value_t import_meta) { ant_offset_t import_off = lkp(js, glob, "import", 6); if (import_off == 0) return; - ant_value_t import_fn = resolveprop(js, mkval(T_PROP, import_off)); + ant_value_t import_fn = propref_load(js, import_off); if (vtype(import_fn) != T_FUNC) return; js_setprop(js, js_func_obj(import_fn), js_mkstr(js, "meta", 4), import_meta); } @@ -10548,7 +11234,7 @@ static ant_value_t builtin_import_meta_resolve(ant_t *js, ant_value_t *args, int ant_value_t filename = js_get(js, import_meta, "filename"); if (vtype(filename) == T_STR) { ant_offset_t n = 0; ant_offset_t off = vstr(js, filename, &n); - return js_esm_resolve_specifier(js, args[0], (const char *)&js->mem[off]); + return js_esm_resolve_specifier(js, args[0], (const char *)(uintptr_t)(off)); } } return js_esm_resolve_specifier(js, args[0], NULL); } @@ -10624,80 +11310,103 @@ void js_module_eval_ctx_pop(ant_t *js, ant_module_t *ctx) { } } -static proxy_data_t *get_proxy_data(ant_value_t obj) { +static ant_proxy_state_t *get_proxy_data(ant_value_t obj) { if (vtype(obj) != T_OBJ) return NULL; - ant_offset_t off = (ant_offset_t)vdata(obj); - proxy_data_t *data = NULL; - HASH_FIND(hh, proxy_registry, &off, sizeof(ant_offset_t), data); - return data; + ant_object_t *ptr = js_obj_ptr(obj); + return ptr ? ptr->proxy_state : NULL; } -bool is_proxy(ant_t *js, ant_value_t obj) { - (void)js; +bool is_proxy(ant_value_t obj) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->is_exotic) return false; return get_proxy_data(obj) != NULL; } +static bool js_is_constructor_impl(ant_t *js, ant_value_t value) { + (void)js; + ant_value_t slow = value; + ant_value_t fast = value; + + while (true) { + uint8_t t = vtype(slow); + if (t == T_FUNC) { + ant_object_t *obj = js_obj_ptr(js_func_obj(slow)); + return obj && obj->is_constructor; + } + if (t != T_OBJ) return false; + + ant_object_t *obj = js_obj_ptr(slow); + if (!obj) return false; + if (obj->is_constructor) return true; + if (!obj->is_exotic) return false; + + ant_proxy_state_t *data = get_proxy_data(slow); + if (!data) return false; + slow = data->target; + + for (int i = 0; i < 2; i++) { + if (vtype(fast) != T_OBJ) { fast = js_mknull(); break; } + ant_object_t *fobj = js_obj_ptr(fast); + if (!fobj || !fobj->is_exotic) { fast = js_mknull(); break; } + ant_proxy_state_t *fdata = get_proxy_data(fast); + if (!fdata) { fast = js_mknull(); break; } + fast = fdata->target; + } + + if (same_object_identity(slow, fast)) return false; + } +} + +bool js_is_constructor(ant_t *js, ant_value_t value) { + return js_is_constructor_impl(js, value); +} + static ant_value_t proxy_read_target(ant_t *js, ant_value_t obj) { - proxy_data_t *data = get_proxy_data(obj); + ant_proxy_state_t *data = get_proxy_data(obj); return data ? data->target : obj; } static ant_offset_t proxy_aware_length(ant_t *js, ant_value_t obj) { - ant_value_t src = is_proxy(js, obj) ? proxy_read_target(js, obj) : obj; - if (vtype(src) == T_ARR) { - ant_offset_t doff = get_dense_buf(js, src); - if (doff) return dense_length(js, doff); - } + ant_value_t src = is_proxy(obj) ? proxy_read_target(js, obj) : obj; + if (vtype(src) == T_ARR) return get_array_length(js, src); ant_offset_t off = lkp_interned(js, src, INTERN_LENGTH, 6); if (off == 0) return 0; - ant_value_t len_val = resolveprop(js, mkval(T_PROP, off)); + ant_value_t len_val = propref_load(js, off); return vtype(len_val) == T_NUM ? (ant_offset_t)tod(len_val) : 0; } static ant_value_t proxy_aware_get_elem(ant_t *js, ant_value_t obj, const char *key, size_t key_len) { - ant_value_t src = is_proxy(js, obj) ? proxy_read_target(js, obj) : obj; + ant_value_t src = is_proxy(obj) ? proxy_read_target(js, obj) : obj; ant_offset_t off = lkp(js, src, key, key_len); - return off ? resolveprop(js, mkval(T_PROP, off)) : js_mkundef(); + return off ? propref_load(js, off) : js_mkundef(); } static ant_value_t throw_proxy_error(ant_t *js, const char *message) { - ant_value_t err_obj = mkobj(js, 0); - js_setprop(js, err_obj, js_mkstr(js, "message", 7), js_mkstr(js, message, strlen(message))); - js_setprop(js, err_obj, js_mkstr(js, "name", 4), js_mkstr(js, "TypeError", 9)); - return js_throw(js, err_obj); + return js_mkerr_typed(js, JS_ERR_TYPE, "%s", message); } -static bool proxy_target_is_extensible(ant_t *js, ant_value_t obj) { +static bool proxy_target_is_extensible(ant_value_t obj) { uint8_t t = vtype(obj); if (t != T_OBJ && t != T_ARR && t != T_FUNC) return false; - ant_value_t as_obj = js_as_obj(obj); - if (js_truthy(js, get_slot(js, as_obj, SLOT_FROZEN))) return false; - if (js_truthy(js, get_slot(js, as_obj, SLOT_SEALED))) return false; - - ant_value_t ext_slot = get_slot(js, as_obj, SLOT_EXTENSIBLE); - if (vtype(ext_slot) != T_UNDEF) return js_truthy(js, ext_slot); - return true; -} - -static ant_offset_t proxy_target_prop_flags(ant_t *js, ant_value_t target, ant_offset_t prop_off) { - if (prop_off == 0) return 0; - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(target)); - ant_offset_t head = loadoff(js, obj_off); - if ((head & ~(3U | FLAGMASK)) == prop_off) return head; - return loadoff(js, prop_off); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr) return false; + if (ptr->frozen || ptr->sealed) return false; + return ptr->extensible != 0; } static bool proxy_target_prop_is_nonconfig(ant_t *js, ant_value_t target, ant_offset_t prop_off) { - return (proxy_target_prop_flags(js, target, prop_off) & NONCONFIGMASK) != 0; + (void)target; + return is_nonconfig_prop(js, prop_off); } static bool proxy_target_prop_is_const(ant_t *js, ant_value_t target, ant_offset_t prop_off) { - return (proxy_target_prop_flags(js, target, prop_off) & CONSTMASK) != 0; + (void)target; + return is_const_prop(js, prop_off); } static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_mkundef(); if (data->revoked) return throw_proxy_error(js, "Cannot perform 'get' on a proxy that has been revoked"); @@ -10709,7 +11418,7 @@ static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size : 0; if (get_trap_off != 0) { - ant_value_t get_trap = resolveprop(js, mkval(T_PROP, get_trap_off)); + ant_value_t get_trap = propref_load(js, get_trap_off); if (vtype(get_trap) == T_FUNC || vtype(get_trap) == T_CFUNC) { ant_value_t key_val = js_mkstr(js, key, key_len); @@ -10720,19 +11429,20 @@ static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size ant_offset_t prop_off = lkp(js, target, key, key_len); if (prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off) && proxy_target_prop_is_const(js, target, prop_off)) { - ant_value_t target_value = resolveprop(js, mkval(T_PROP, prop_off)); + ant_value_t target_value = propref_load(js, prop_off); if (!strict_eq_values(js, result, target_value)) return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned invalid value for non-configurable, non-writable property"); } - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(target)), key, key_len); - if (desc && !desc->configurable) { - if (!desc->has_getter && !desc->has_setter && !desc->writable && prop_off != 0) { - ant_value_t target_value = resolveprop(js, mkval(T_PROP, prop_off)); + prop_meta_t meta; + bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta); + if (has_meta && !meta.configurable) { + if (!meta.has_getter && !meta.has_setter && !meta.writable && prop_off != 0) { + ant_value_t target_value = propref_load(js, prop_off); if (!strict_eq_values(js, result, target_value)) return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned invalid value for non-configurable, non-writable property"); } - if ((desc->has_getter || desc->has_setter) && !desc->has_getter && vtype(result) != T_UNDEF) + if ((meta.has_getter || meta.has_setter) && !meta.has_getter && vtype(result) != T_UNDEF) return js_mkerr_typed(js, JS_ERR_TYPE, "'get' on proxy: trap returned non-undefined for property with undefined getter"); } @@ -10746,16 +11456,16 @@ static ant_value_t proxy_get(ant_t *js, ant_value_t proxy, const char *key, size key_buf[len] = '\0'; ant_offset_t off = lkp(js, target, key_buf, len); - if (off != 0) return resolveprop(js, mkval(T_PROP, off)); + if (off != 0) return propref_load(js, off); ant_offset_t proto_off = lkp_proto(js, target, key_buf, len); - if (proto_off != 0) return resolveprop(js, mkval(T_PROP, proto_off)); + if (proto_off != 0) return propref_load(js, proto_off); return js_mkundef(); } static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size_t key_len, ant_value_t value) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_mkundef(); if (data->revoked) return throw_proxy_error(js, "Cannot perform 'set' on a proxy that has been revoked"); @@ -10764,7 +11474,7 @@ static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size ant_offset_t set_trap_off = vtype(handler) == T_OBJ ? lkp_interned(js, handler, INTERN_SET, 3) : 0; if (set_trap_off != 0) { - ant_value_t set_trap = resolveprop(js, mkval(T_PROP, set_trap_off)); + ant_value_t set_trap = propref_load(js, set_trap_off); if (vtype(set_trap) == T_FUNC || vtype(set_trap) == T_CFUNC) { ant_value_t key_val = js_mkstr(js, key, key_len); ant_value_t args[4] = { target, key_val, value, proxy }; @@ -10774,19 +11484,20 @@ static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size ant_offset_t prop_off = lkp(js, target, key, key_len); if (prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off) && proxy_target_prop_is_const(js, target, prop_off)) { - ant_value_t target_value = resolveprop(js, mkval(T_PROP, prop_off)); + ant_value_t target_value = propref_load(js, prop_off); if (!strict_eq_values(js, value, target_value)) return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for non-configurable, non-writable property with different value"); } - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(target)), key, key_len); - if (desc && !desc->configurable) { - if (!desc->has_getter && !desc->has_setter && !desc->writable && prop_off != 0) { - ant_value_t target_value = resolveprop(js, mkval(T_PROP, prop_off)); + prop_meta_t meta; + bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta); + if (has_meta && !meta.configurable) { + if (!meta.has_getter && !meta.has_setter && !meta.writable && prop_off != 0) { + ant_value_t target_value = propref_load(js, prop_off); if (!strict_eq_values(js, value, target_value)) return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for non-configurable, non-writable property with different value"); } - if ((desc->has_getter || desc->has_setter) && !desc->has_setter) + if ((meta.has_getter || meta.has_setter) && !meta.has_setter) return js_mkerr_typed(js, JS_ERR_TYPE, "'set' on proxy: trap returned truthy for property with undefined setter"); } } @@ -10800,7 +11511,7 @@ static ant_value_t proxy_set(ant_t *js, ant_value_t proxy, const char *key, size } static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_false; if (data->revoked) return throw_proxy_error(js, "Cannot perform 'has' on a proxy that has been revoked"); @@ -10809,7 +11520,7 @@ static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size ant_offset_t has_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "has", 3) : 0; if (has_trap_off != 0) { - ant_value_t has_trap = resolveprop(js, mkval(T_PROP, has_trap_off)); + ant_value_t has_trap = propref_load(js, has_trap_off); if (vtype(has_trap) == T_FUNC || vtype(has_trap) == T_CFUNC) { ant_value_t key_val = js_mkstr(js, key, key_len); ant_value_t args[2] = { target, key_val }; @@ -10818,13 +11529,14 @@ static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size if (!js_truthy(js, result)) { ant_offset_t prop_off = lkp(js, target, key, key_len); - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(target)), key, key_len); - bool has_own = (prop_off != 0) || (desc != NULL); + prop_meta_t meta; + bool has_meta = lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta); + bool has_own = (prop_off != 0) || has_meta; - if ((prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off)) || (desc && !desc->configurable)) + if ((prop_off != 0 && proxy_target_prop_is_nonconfig(js, target, prop_off)) || (has_meta && !meta.configurable)) return js_mkerr_typed(js, JS_ERR_TYPE, "'has' on proxy: trap returned falsy for non-configurable property"); - if (has_own && !proxy_target_is_extensible(js, target)) + if (has_own && !proxy_target_is_extensible(target)) return js_mkerr_typed(js, JS_ERR_TYPE, "'has' on proxy: trap returned falsy for existing property on non-extensible target"); } @@ -10842,7 +11554,7 @@ static ant_value_t proxy_has(ant_t *js, ant_value_t proxy, const char *key, size } static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, size_t key_len) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_true; if (data->revoked) return throw_proxy_error(js, "Cannot perform 'deleteProperty' on a proxy that has been revoked"); @@ -10851,7 +11563,7 @@ static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, s ant_offset_t delete_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "deleteProperty", 14) : 0; if (delete_trap_off != 0) { - ant_value_t delete_trap = resolveprop(js, mkval(T_PROP, delete_trap_off)); + ant_value_t delete_trap = propref_load(js, delete_trap_off); if (vtype(delete_trap) == T_FUNC || vtype(delete_trap) == T_CFUNC) { ant_value_t key_val = js_mkstr(js, key, key_len); ant_value_t args[2] = { target, key_val }; @@ -10861,8 +11573,8 @@ static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, s ant_offset_t prop_off = lkp(js, target, key, key_len); if (prop_off != 0 && is_nonconfig_prop(js, prop_off)) return js_mkerr_typed(js, JS_ERR_TYPE, "'deleteProperty' on proxy: trap returned truthy for non-configurable property"); - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(target)), key, key_len); - if (desc && !desc->configurable) + prop_meta_t meta; + if (lookup_string_prop_meta(js, js_as_obj(target), key, key_len, &meta) && !meta.configurable) return js_mkerr_typed(js, JS_ERR_TYPE, "'deleteProperty' on proxy: trap returned truthy for non-configurable property"); } return result; @@ -10875,7 +11587,7 @@ static ant_value_t proxy_delete(ant_t *js, ant_value_t proxy, const char *key, s } static ant_value_t proxy_get_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_mkundef(); if (data->revoked) return throw_proxy_error(js, "Cannot perform 'get' on a proxy that has been revoked"); @@ -10885,7 +11597,7 @@ static ant_value_t proxy_get_val(ant_t *js, ant_value_t proxy, ant_value_t key_v ant_offset_t get_trap_off = vtype(handler) == T_OBJ ? lkp_interned(js, handler, INTERN_GET, 3) : 0; if (get_trap_off != 0) { - ant_value_t get_trap = resolveprop(js, mkval(T_PROP, get_trap_off)); + ant_value_t get_trap = propref_load(js, get_trap_off); if (vtype(get_trap) == T_FUNC || vtype(get_trap) == T_CFUNC) { ant_value_t args[3] = { target, key_val, proxy }; return sv_vm_call(js->vm, js, get_trap, js_mkundef(), args, 3, NULL, false); @@ -10894,14 +11606,14 @@ static ant_value_t proxy_get_val(ant_t *js, ant_value_t proxy, ant_value_t key_v if (vtype(key_val) == T_SYMBOL) { ant_offset_t off = lkp_sym_proto(js, target, (ant_offset_t)vdata(key_val)); - return off != 0 ? resolveprop(js, mkval(T_PROP, off)) : js_mkundef(); + return off != 0 ? propref_load(js, off) : js_mkundef(); } return proxy_get(js, proxy, "", 0); } static ant_value_t proxy_has_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_false; if (data->revoked) return throw_proxy_error(js, "Cannot perform 'has' on a proxy that has been revoked"); @@ -10910,7 +11622,7 @@ static ant_value_t proxy_has_val(ant_t *js, ant_value_t proxy, ant_value_t key_v ant_offset_t has_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "has", 3) : 0; if (has_trap_off != 0) { - ant_value_t has_trap = resolveprop(js, mkval(T_PROP, has_trap_off)); + ant_value_t has_trap = propref_load(js, has_trap_off); if (vtype(has_trap) == T_FUNC || vtype(has_trap) == T_CFUNC) { ant_value_t args[2] = { target, key_val }; return sv_vm_call(js->vm, js, has_trap, js_mkundef(), args, 2, NULL, false); @@ -10925,7 +11637,7 @@ static ant_value_t proxy_has_val(ant_t *js, ant_value_t proxy, ant_value_t key_v } static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t key_val) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_true; if (data->revoked) return throw_proxy_error(js, "Cannot perform 'deleteProperty' on a proxy that has been revoked"); @@ -10934,7 +11646,7 @@ static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t ke ant_offset_t delete_trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "deleteProperty", 14) : 0; if (delete_trap_off != 0) { - ant_value_t delete_trap = resolveprop(js, mkval(T_PROP, delete_trap_off)); + ant_value_t delete_trap = propref_load(js, delete_trap_off); if (vtype(delete_trap) == T_FUNC || vtype(delete_trap) == T_CFUNC) { ant_value_t args[2] = { target, key_val }; ant_value_t result = sv_vm_call(js->vm, js, delete_trap, js_mkundef(), args, 2, NULL, false); @@ -10949,27 +11661,13 @@ static ant_value_t proxy_delete_val(ant_t *js, ant_value_t proxy, ant_value_t ke } if (vtype(key_val) == T_SYMBOL) { - ant_offset_t sym_off = (ant_offset_t)vdata(key_val); - ant_offset_t prop_off = lkp_sym(js, target, sym_off); - if (prop_off != 0) { - ant_offset_t obj_off = (ant_offset_t)vdata(js_as_obj(target)); - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - if (first_prop == prop_off) { - unlink_prop(js, obj_off, prop_off, 0); - } else { - for (ant_offset_t prev = first_prop; prev != 0; ) { - ant_offset_t np = loadoff(js, prev) & ~(3U | FLAGMASK); - if (np == prop_off) { unlink_prop(js, obj_off, prop_off, prev); break; } - prev = np; - } - } - } + return js_delete_sym_prop(js, target, key_val); } return js_true; } ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, ant_value_t *args, int argc) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_mkerr_typed(js, JS_ERR_TYPE, "object is not a function"); if (data->revoked) return throw_proxy_error(js, "Cannot perform 'apply' on a proxy that has been revoked"); @@ -10977,12 +11675,12 @@ ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, a ant_value_t handler = data->handler; uint8_t target_type = vtype(target); - if (target_type != T_FUNC && target_type != T_CFUNC && !(target_type == T_OBJ && is_proxy(js, target))) + if (target_type != T_FUNC && target_type != T_CFUNC && !(target_type == T_OBJ && is_proxy(target))) return js_mkerr_typed(js, JS_ERR_TYPE, "%s is not a function", typestr(target_type)); ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "apply", 5) : 0; if (trap_off != 0) { - ant_value_t trap = resolveprop(js, mkval(T_PROP, trap_off)); + ant_value_t trap = propref_load(js, trap_off); if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) { ant_value_t args_arr = mkarr(js); for (int i = 0; i < argc; i++) @@ -10996,19 +11694,23 @@ ant_value_t js_proxy_apply(ant_t *js, ant_value_t proxy, ant_value_t this_arg, a } ant_value_t js_proxy_construct(ant_t *js, ant_value_t proxy, ant_value_t *args, int argc, ant_value_t new_target) { - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(proxy); if (!data) return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); if (data->revoked) return throw_proxy_error(js, "Cannot perform 'construct' on a proxy that has been revoked"); ant_value_t target = data->target; ant_value_t handler = data->handler; + if (!js_is_constructor(js, target)) + return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); + if (vtype(target) == T_OBJ && is_proxy(target)) + return js_proxy_construct(js, target, args, argc, new_target); if (vtype(target) != T_FUNC) return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); ant_offset_t trap_off = vtype(handler) == T_OBJ ? lkp(js, handler, "construct", 9) : 0; if (trap_off != 0) { - ant_value_t trap = resolveprop(js, mkval(T_PROP, trap_off)); + ant_value_t trap = propref_load(js, trap_off); if (vtype(trap) == T_FUNC || vtype(trap) == T_CFUNC) { ant_value_t args_arr = mkarr(js); for (int i = 0; i < argc; i++) @@ -11024,7 +11726,7 @@ ant_value_t js_proxy_construct(ant_t *js, ant_value_t proxy, ant_value_t *args, ant_value_t obj = mkobj(js, 0); ant_value_t proto = js_getprop_fallback(js, target, "prototype"); - if (is_object_type(proto)) js_set_proto(js, obj, proto); + if (is_object_type(proto)) js_set_proto_init(obj, proto); ant_value_t saved = js->new_target; js->new_target = new_target; ant_value_t ctor_this = obj; @@ -11036,17 +11738,19 @@ ant_value_t js_proxy_construct(ant_t *js, ant_value_t proxy, ant_value_t *args, static ant_value_t mkproxy(ant_t *js, ant_value_t target, ant_value_t handler) { ant_value_t proxy_obj = mkobj(js, 0); - ant_offset_t off = (ant_offset_t)vdata(proxy_obj); - - proxy_data_t *data = (proxy_data_t *)ant_calloc(sizeof(proxy_data_t)); + ant_object_t *proxy_ptr = js_obj_ptr(proxy_obj); + if (!proxy_ptr) return js_mkerr(js, "out of memory"); + + ant_proxy_state_t *data = (ant_proxy_state_t *)ant_calloc(sizeof(ant_proxy_state_t)); if (!data) return js_mkerr(js, "out of memory"); - - data->obj_offset = off; + data->target = target; data->handler = handler; data->revoked = false; - - HASH_ADD(hh, proxy_registry, obj_offset, sizeof(ant_offset_t), data); + + proxy_ptr->is_exotic = 1; + js_mark_constructor(proxy_obj, js_is_constructor(js, target)); + proxy_ptr->proxy_state = data; return proxy_obj; } @@ -11079,11 +11783,10 @@ static ant_value_t builtin_Proxy(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t proxy_revoke_fn(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t func = js->current_func; - ant_value_t ref_slot = get_slot(js, func, SLOT_PROXY_REF); + ant_value_t ref_slot = get_slot(func, SLOT_PROXY_REF); if (vtype(ref_slot) != T_UNDEF && vdata(ref_slot) != 0) { - ant_value_t proxy = resolveprop(js, mkval(T_PROP, (ant_offset_t)vdata(ref_slot))); - proxy_data_t *data = get_proxy_data(proxy); + ant_proxy_state_t *data = get_proxy_data(ref_slot); if (data) data->revoked = true; } @@ -11095,8 +11798,8 @@ static ant_value_t builtin_Proxy_revocable(ant_t *js, ant_value_t *args, int nar if (is_err(proxy)) return proxy; ant_value_t revoke_obj = mkobj(js, 0); - set_slot(js, revoke_obj, SLOT_CFUNC, js_mkfun(proxy_revoke_fn)); - set_slot(js, revoke_obj, SLOT_PROXY_REF, proxy); + set_slot(revoke_obj, SLOT_CFUNC, js_mkfun(proxy_revoke_fn)); + set_slot(revoke_obj, SLOT_PROXY_REF, proxy); ant_value_t revoke_func = js_obj_to_func(revoke_obj); @@ -11116,15 +11819,30 @@ ant_t *js_create(void *buf, size_t len) { intern_init(); ant_t *js = NULL; - if (len < sizeof(*js) + esize(T_OBJ)) return js; + if (len < sizeof(*js)) return js; memset(buf, 0, len); js = (ant_t *) buf; - js->mem = (uint8_t *) (js + 1); - js->size = (ant_offset_t) (len - sizeof(*js)); - js->brk = NANBOX_HEAP_OFFSET; + rt->js = js; + if (!fixed_arena_init(&js->obj_arena, sizeof(ant_object_t), offsetof(ant_object_t, mark_epoch), ANT_ARENA_MAX)) return NULL; + if (!fixed_arena_init(&js->closure_arena, sizeof(sv_closure_t), offsetof(sv_closure_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) { + fixed_arena_destroy(&js->obj_arena); + return NULL; + } + if (!fixed_arena_init(&js->upvalue_arena, sizeof(sv_upvalue_t), offsetof(sv_upvalue_t, gc_epoch), ANT_CLOSURE_ARENA_MAX)) { + fixed_arena_destroy(&js->closure_arena); + fixed_arena_destroy(&js->obj_arena); + return NULL; + } + js->c_root_cap = 64; + js->c_roots = calloc(js->c_root_cap, sizeof(*js->c_roots)); + if (!js->c_roots) { + fixed_arena_destroy(&js->upvalue_arena); + fixed_arena_destroy(&js->closure_arena); + fixed_arena_destroy(&js->obj_arena); + return NULL; + } js->global = mkobj(js, 0); - js->size = js->size / 8U * 8U; js->this_val = js->global; js->new_target = js_mkundef(); js->length_str = ANT_STRING("length"); @@ -11157,13 +11875,13 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t function_proto_obj = js_mkobj(js); set_proto(js, function_proto_obj, object_proto); - set_slot(js, function_proto_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty)); + set_slot(function_proto_obj, SLOT_CFUNC, js_mkfun(builtin_function_empty)); js_setprop(js, function_proto_obj, ANT_STRING("call"), js_mkfun(builtin_function_call)); js_setprop(js, function_proto_obj, ANT_STRING("apply"), js_mkfun(builtin_function_apply)); js_setprop(js, function_proto_obj, ANT_STRING("bind"), js_mkfun(builtin_function_bind)); js_setprop(js, function_proto_obj, ANT_STRING("toString"), js_mkfun(builtin_function_toString)); ant_value_t function_proto = js_obj_to_func(function_proto_obj); - set_slot(js, glob, SLOT_FUNC_PROTO, function_proto); + set_slot(glob, SLOT_FUNC_PROTO, function_proto); ant_value_t array_proto = js_mkobj(js); set_proto(js, array_proto, object_proto); @@ -11262,7 +11980,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t err_ctor_obj = mkobj(js, 0); set_proto(js, err_ctor_obj, function_proto); - set_slot(js, err_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Error)); + set_slot(err_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Error)); js_setprop_nonconfigurable(js, err_ctor_obj, "prototype", 9, error_proto); js_setprop(js, err_ctor_obj, ANT_STRING("name"), ANT_STRING("Error")); ant_value_t err_ctor_func = js_obj_to_func(err_ctor_obj); @@ -11276,7 +11994,7 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, proto, ANT_STRING("name"), ANT_STRING(name_str)); \ ant_value_t ctor = mkobj(js, 0); \ set_proto(js, ctor, function_proto); \ - set_slot(js, ctor, SLOT_CFUNC, js_mkfun(builtin_Error)); \ + set_slot(ctor, SLOT_CFUNC, js_mkfun(builtin_Error)); \ js_setprop_nonconfigurable(js, ctor, "prototype", 9, proto); \ js_setprop(js, ctor, ANT_STRING("name"), ANT_STRING(name_str)); \ ant_value_t ctor_func = js_obj_to_func(ctor); \ @@ -11300,7 +12018,7 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, proto, ANT_STRING("name"), ANT_STRING("AggregateError")); ant_value_t ctor = mkobj(js, 0); set_proto(js, ctor, function_proto); - set_slot(js, ctor, SLOT_CFUNC, js_mkfun(builtin_AggregateError)); + set_slot(ctor, SLOT_CFUNC, js_mkfun(builtin_AggregateError)); js_setprop_nonconfigurable(js, ctor, "prototype", 9, proto); js_setprop(js, ctor, ANT_STRING("name"), ANT_STRING("AggregateError")); js_setprop(js, proto, ANT_STRING("constructor"), js_obj_to_func(ctor)); @@ -11312,11 +12030,11 @@ ant_t *js_create(void *buf, size_t len) { js_setprop(js, promise_proto, js_mkstr(js, "then", 4), js_mkfun(builtin_promise_then)); js_setprop(js, promise_proto, js_mkstr(js, "catch", 5), js_mkfun(builtin_promise_catch)); js_setprop(js, promise_proto, js_mkstr(js, "finally", 7), js_mkfun(builtin_promise_finally)); - // Symbol.toStringTag is set in init_symbol_module after symbols are initialized ant_value_t obj_func_obj = mkobj(js, 0); set_proto(js, obj_func_obj, function_proto); - set_slot(js, obj_func_obj, SLOT_BUILTIN, tov(BUILTIN_OBJECT)); + set_slot(obj_func_obj, SLOT_BUILTIN, tov(BUILTIN_OBJECT)); + js_mark_constructor(obj_func_obj, true); js_setprop(js, obj_func_obj, js_mkstr(js, "keys", 4), js_mkfun(builtin_object_keys)); js_setprop(js, obj_func_obj, js_mkstr(js, "values", 6), js_mkfun(builtin_object_values)); js_setprop(js, obj_func_obj, js_mkstr(js, "entries", 7), js_mkfun(builtin_object_entries)); @@ -11346,7 +12064,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t func_ctor_obj = mkobj(js, 0); set_proto(js, func_ctor_obj, function_proto); - set_slot(js, func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Function)); + set_slot(func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Function)); js_setprop_nonconfigurable(js, func_ctor_obj, "prototype", 9, function_proto); js_setprop(js, func_ctor_obj, js->length_str, tov(1.0)); js_set_descriptor(js, func_ctor_obj, "length", 6, JS_DESC_C); @@ -11356,13 +12074,13 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t async_func_proto_obj = js_mkobj(js); set_proto(js, async_func_proto_obj, function_proto); - set_slot(js, async_func_proto_obj, SLOT_ASYNC, js_true); + set_slot(async_func_proto_obj, SLOT_ASYNC, js_true); ant_value_t async_func_proto = js_obj_to_func(async_func_proto_obj); - set_slot(js, glob, SLOT_ASYNC_PROTO, async_func_proto); + set_slot(glob, SLOT_ASYNC_PROTO, async_func_proto); ant_value_t async_func_ctor_obj = mkobj(js, 0); set_proto(js, async_func_ctor_obj, function_proto); - set_slot(js, async_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_AsyncFunction)); + set_slot(async_func_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_AsyncFunction)); js_setprop_nonconfigurable(js, async_func_ctor_obj, "prototype", 9, async_func_proto); js_setprop(js, async_func_ctor_obj, js->length_str, tov(1.0)); js_set_descriptor(js, async_func_ctor_obj, "length", 6, JS_DESC_C); @@ -11374,7 +12092,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t str_ctor_obj = mkobj(js, 0); set_proto(js, str_ctor_obj, function_proto); - set_slot(js, str_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_String)); + set_slot(str_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_String)); js_setprop_nonconfigurable(js, str_ctor_obj, "prototype", 9, string_proto); js_setprop(js, str_ctor_obj, js_mkstr(js, "fromCharCode", 12), js_mkfun(builtin_string_fromCharCode)); js_setprop(js, str_ctor_obj, js_mkstr(js, "fromCodePoint", 13), js_mkfun(builtin_string_fromCodePoint)); @@ -11386,7 +12104,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t number_ctor_obj = mkobj(js, 0); set_proto(js, number_ctor_obj, function_proto); - set_slot(js, number_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Number)); + set_slot(number_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Number)); js_setprop(js, number_ctor_obj, js_mkstr(js, "isNaN", 5), js_mkfun(builtin_Number_isNaN)); js_setprop(js, number_ctor_obj, js_mkstr(js, "isFinite", 8), js_mkfun(builtin_Number_isFinite)); js_setprop(js, number_ctor_obj, js_mkstr(js, "isInteger", 9), js_mkfun(builtin_Number_isInteger)); @@ -11410,7 +12128,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t bool_ctor_obj = mkobj(js, 0); set_proto(js, bool_ctor_obj, function_proto); - set_slot(js, bool_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Boolean)); + set_slot(bool_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Boolean)); js_setprop_nonconfigurable(js, bool_ctor_obj, "prototype", 9, boolean_proto); js_setprop(js, bool_ctor_obj, ANT_STRING("name"), ANT_STRING("Boolean")); ant_value_t bool_ctor_func = js_obj_to_func(bool_ctor_obj); @@ -11418,7 +12136,7 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t arr_ctor_obj = mkobj(js, 0); set_proto(js, arr_ctor_obj, function_proto); - set_slot(js, arr_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Array)); + set_slot(arr_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Array)); js_setprop_nonconfigurable(js, arr_ctor_obj, "prototype", 9, array_proto); js_setprop(js, arr_ctor_obj, js_mkstr(js, "isArray", 7), js_mkfun(builtin_Array_isArray)); js_setprop(js, arr_ctor_obj, js_mkstr(js, "from", 4), js_mkfun(builtin_Array_from)); @@ -11431,14 +12149,15 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t proxy_ctor_obj = mkobj(js, 0); set_proto(js, proxy_ctor_obj, function_proto); - set_slot(js, proxy_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Proxy)); + set_slot(proxy_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Proxy)); + js_mark_constructor(proxy_ctor_obj, true); js_setprop(js, proxy_ctor_obj, js_mkstr(js, "revocable", 9), js_mkfun(builtin_Proxy_revocable)); js_setprop(js, proxy_ctor_obj, ANT_STRING("name"), ANT_STRING("Proxy")); js_setprop(js, glob, js_mkstr(js, "Proxy", 5), js_obj_to_func(proxy_ctor_obj)); ant_value_t p_ctor_obj = mkobj(js, 0); set_proto(js, p_ctor_obj, function_proto); - set_slot(js, p_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Promise)); + set_slot(p_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Promise)); js_setprop(js, p_ctor_obj, js_mkstr(js, "resolve", 7), js_mkfun(builtin_Promise_resolve)); js_setprop(js, p_ctor_obj, js_mkstr(js, "reject", 6), js_mkfun(builtin_Promise_reject)); js_setprop(js, p_ctor_obj, js_mkstr(js, "try", 3), js_mkfun(builtin_Promise_try)); @@ -11467,14 +12186,14 @@ ant_t *js_create(void *buf, size_t len) { ant_value_t import_obj = mkobj(js, 0); set_proto(js, import_obj, function_proto); - set_slot(js, import_obj, SLOT_CFUNC, js_mkfun(builtin_import)); + set_slot(import_obj, SLOT_CFUNC, js_mkfun(builtin_import)); js_setprop(js, glob, js_mkstr(js, "import", 6), js_obj_to_func(import_obj)); js_setprop(js, object_proto, js_mkstr(js, "constructor", 11), obj_func); js_set_descriptor(js, object_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); js_setprop(js, function_proto, js_mkstr(js, "constructor", 11), func_ctor_func); - js_set_descriptor(js, function_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); + js_set_descriptor(js, js_as_obj(function_proto), "constructor", 11, JS_DESC_W | JS_DESC_C); js_setprop(js, array_proto, js_mkstr(js, "constructor", 11), arr_ctor_func); js_set_descriptor(js, array_proto, "constructor", 11, JS_DESC_W | JS_DESC_C); @@ -11498,39 +12217,15 @@ ant_t *js_create(void *buf, size_t len) { } ant_t *js_create_dynamic() { - void *init_buf = ant_calloc(ANT_ARENA_MIN); - if (init_buf == NULL) return NULL; - - ant_t *js = js_create(init_buf, ANT_ARENA_MIN); - if (js == NULL) { free(init_buf); return NULL; } - - uint8_t *arena = (uint8_t *)ant_arena_reserve(ANT_ARENA_MAX); - if (arena == NULL) { free(init_buf); return NULL; } - - if (ant_arena_commit(arena, 0, js->size) != 0) { - ant_arena_free(arena, ANT_ARENA_MAX); - free(init_buf); return NULL; - } - - memcpy(arena, js->mem, js->brk); - js->mem = arena; - - js->owns_mem = true; - js->max_size = (ant_offset_t) ANT_ARENA_MAX; - - ant_t *new_js = (ant_t *)malloc(sizeof(ant_t)); - if (new_js == NULL) { - ant_arena_free(arena, ANT_ARENA_MAX); - free(init_buf); + ant_t *js = (ant_t *)calloc(1, sizeof(*js)); + if (js == NULL) return NULL; + if (js_create(js, sizeof(*js)) == NULL) { + free(js); return NULL; } - - memcpy(new_js, js, sizeof(ant_t)); - free(init_buf); - - new_js->vm = sv_vm_create(new_js, SV_VM_MAIN); - - return new_js; + js->owns_mem = true; + js->vm = sv_vm_create(js, SV_VM_MAIN); + return js; } void js_destroy(ant_t *js) { @@ -11544,15 +12239,38 @@ void js_destroy(ant_t *js) { js_esm_cleanup_module_cache(); code_arena_reset(); cleanup_buffer_module(); - cleanup_collections_module(); cleanup_lmdb_module(); - - if (js->owns_mem) { - ant_arena_free(js->mem, js->max_size); - free(js); - } - + + ant_object_t *lists[] = { js->objects, js->objects_old, js->permanent_objects }; + for (int i = 0; i < 3; i++) + for (ant_object_t *obj = lists[i]; obj;) { + ant_object_t *next = obj->next; + gc_object_free(js, obj); + obj = next; + } + + js->objects = NULL; + js->objects_old = NULL; + js->permanent_objects = NULL; + fixed_arena_destroy(&js->obj_arena); + fixed_arena_destroy(&js->closure_arena); + fixed_arena_destroy(&js->upvalue_arena); + + free(js->prop_refs); + js->prop_refs = NULL; + js->prop_refs_len = js->prop_refs_cap = 0; + free(js->c_roots); + js->c_roots = NULL; + js->c_root_count = js->c_root_cap = 0; + + js_pool_destroy(&js->pool.rope); + js_pool_destroy(&js->pool.symbol); + js_pool_destroy(&js->pool.permanent); + js_class_pool_destroy(&js->pool.bigint); + js_class_pool_destroy(&js->pool.string); + destroy_runtime(js); + if (js->owns_mem) free(js); } inline double js_getnum(ant_value_t value) { return tod(value); } @@ -11575,8 +12293,8 @@ ant_value_t js_heavy_mkfun(ant_t *js, ant_value_t (*fn)(ant_t *, ant_value_t *, ant_value_t cfunc = js_mkfun(fn); ant_value_t fn_obj = mkobj(js, 0); - set_slot(js, fn_obj, SLOT_CFUNC, cfunc); - set_slot(js, fn_obj, SLOT_DATA, data); + set_slot(fn_obj, SLOT_CFUNC, cfunc); + set_slot(fn_obj, SLOT_DATA, data); return js_obj_to_func(fn_obj); } @@ -11591,7 +12309,7 @@ void js_set(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) { js_mkerr(js, "assignment to constant"); return; } - saveval(js, existing + sizeof(ant_offset_t) * 2, val); + js_saveval(js, existing, val); } else { ant_value_t key_str = js_mkstr(js, key, key_len); mkprop(js, obj, key_str, val, 0); @@ -11604,7 +12322,7 @@ void js_set(ant_t *js, ant_value_t obj, const char *key, ant_value_t val) { js_mkerr(js, "assignment to constant"); return; } - saveval(js, existing + sizeof(ant_offset_t) * 2, val); + js_saveval(js, existing, val); } else { ant_value_t key_str = js_mkstr(js, key, key_len); mkprop(js, func_obj, key_str, val, 0); @@ -11622,7 +12340,7 @@ void js_set_sym(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t val) { ant_offset_t existing = lkp_sym(js, obj, sym_off); if (existing > 0) { if (is_const_prop(js, existing)) return; - saveval(js, existing + sizeof(ant_offset_t) * 2, val); + js_saveval(js, existing, val); } else mkprop(js, obj, sym, val, 0); } @@ -11645,91 +12363,34 @@ ant_value_t js_get_sym(ant_t *js, ant_value_t obj, ant_value_t sym) { obj = js_as_obj(obj); } - if (is_proxy(js, obj)) return proxy_get_val(js, obj, sym); + if (is_proxy(obj)) return proxy_get_val(js, obj, sym); ant_value_t cur = obj; - for (int i = 0; i < MAX_PROTO_CHAIN_DEPTH; i++) { - ant_offset_t cur_off = (ant_offset_t)vdata(cur); - descriptor_entry_t *sd = lookup_sym_descriptor(cur_off, sym_off); - if (sd && sd->has_getter) { - ant_value_t g = sd->getter; - if (vtype(g) == T_FUNC || vtype(g) == T_CFUNC) - return sv_vm_call(js->vm, js, g, receiver, NULL, 0, NULL, false); - return js_mkundef(); + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + while (is_object_type(cur)) { + ant_value_t cur_obj = js_as_obj(cur); + prop_meta_t meta; + if (lookup_symbol_prop_meta(cur_obj, sym_off, &meta)) { + if (meta.has_getter) { + ant_value_t g = meta.getter; + if (vtype(g) == T_FUNC || vtype(g) == T_CFUNC) + return sv_vm_call(js->vm, js, g, receiver, NULL, 0, NULL, false); + return js_mkundef(); + } + if (meta.has_setter && !meta.has_getter) return js_mkundef(); + break; } - ant_value_t proto = get_proto(js, cur); + + ant_value_t proto = get_proto(js, cur_obj); if (!is_object_type(proto)) break; cur = js_as_obj(proto); + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } ant_offset_t off = lkp_sym_proto(js, obj, sym_off); if (off == 0) return js_mkundef(); - return resolveprop(js, mkval(T_PROP, off)); -} - -bool js_del(ant_t *js, ant_value_t obj, const char *key) { - size_t len = strlen(key); - ant_offset_t obj_off; - - if (vtype(obj) == T_OBJ) { - obj_off = (ant_offset_t)vdata(obj); - } else if (vtype(obj) == T_ARR || vtype(obj) == T_FUNC) { - obj_off = (ant_offset_t)vdata(js_as_obj(obj)); - if (vtype(obj) == T_ARR) { - unsigned long del_idx; - ant_offset_t arr_len = get_array_length(js, obj); - if (parse_array_index(key, len, arr_len, &del_idx)) { - arr_del(js, obj, (ant_offset_t)del_idx); - return true; - } - } - obj = mkval(T_OBJ, obj_off); - } else { - return false; - } - - ant_offset_t prop_off = lkp(js, obj, key, len); - if (prop_off == 0) return true; - if (is_nonconfig_prop(js, prop_off)) return false; - - descriptor_entry_t *desc = lookup_descriptor(js, obj_off, key, len); - if (desc && !desc->configurable) return false; - - ant_offset_t first_prop = loadoff(js, obj_off) & ~(3U | FLAGMASK); - ant_offset_t tail = loadoff(js, obj_off + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - - if (first_prop == prop_off) { - ant_offset_t deleted_next = loadoff(js, prop_off) & ~FLAGMASK; - ant_offset_t current = loadoff(js, obj_off); - - saveoff(js, obj_off, (deleted_next & ~3ULL) | (current & (FLAGMASK | 3ULL))); - if (tail == prop_off) saveoff(js, obj_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), 0); - - invalidate_prop_cache(js, obj_off, prop_off); - js->needs_gc = true; - - return true; - } - - ant_offset_t prev = first_prop; - while (prev != 0 && prev < js->brk) { - ant_offset_t next = loadoff(js, prev) & ~(3U | FLAGMASK); - if (next == prop_off) { - ant_offset_t deleted_next = loadoff(js, prop_off) & ~(3U | FLAGMASK); - ant_offset_t prev_flags = loadoff(js, prev) & FLAGMASK; - - saveoff(js, prev, deleted_next | prev_flags); - if (tail == prop_off) saveoff(js, obj_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), prev); - - invalidate_prop_cache(js, obj_off, prop_off); - js->needs_gc = true; - - return true; - } - prev = next; - } - - return false; + return propref_load(js, off); } static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t *out) { @@ -11748,7 +12409,7 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t ant_value_t func_obj = js_func_obj(obj); ant_value_t import_meta = js_get_current_import_meta(js); if (key_len == 4 && memcmp(key, "meta", 4) == 0 && vtype(import_meta) != T_UNDEF) { - ant_value_t cfunc = js_get_slot(js, func_obj, SLOT_CFUNC); + ant_value_t cfunc = js_get_slot(func_obj, SLOT_CFUNC); if (vtype(cfunc) == T_CFUNC && js_as_cfunc(cfunc) == builtin_import) { *out = import_meta; return true; @@ -11764,8 +12425,8 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t return false; } - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(func_obj), key, key_len); - if (desc && desc->has_getter) { + const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off); + if (prop_meta && prop_meta->has_getter) { ant_value_t accessor_result; if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) { *out = accessor_result; @@ -11773,21 +12434,21 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t } } - *out = resolveprop(js, mkval(T_PROP, off)); + *out = propref_load(js, off); return true; } - if (vtype(obj) == T_ARR) { + if (array_obj_ptr(obj)) { if (((key_len == 6 && memcmp(key, "callee", 6) == 0) || (key_len == 6 && memcmp(key, "caller", 6) == 0)) && - vtype(get_slot(js, obj, SLOT_STRICT_ARGS)) != T_UNDEF) { + vtype(get_slot(obj, SLOT_STRICT_ARGS)) != T_UNDEF) { *out = js_mkerr_typed(js, JS_ERR_TYPE, "'%.*s' not allowed on strict arguments", (int)key_len, key); return true; } - if (key_len == 6 && memcmp(key, "length", 6) == 0) { + if (is_length_key(key, key_len)) { *out = tov((double)get_array_length(js, obj)); return true; } @@ -11809,34 +12470,34 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t } return false; } - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(arr_obj), key, key_len); - if (desc && desc->has_getter) { + const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off); + if (prop_meta && prop_meta->has_getter) { ant_value_t accessor_result; if (try_accessor_getter(js, arr_obj, key, key_len, &accessor_result)) { *out = accessor_result; return true; } } - *out = resolveprop(js, mkval(T_PROP, off)); + *out = propref_load(js, off); return true; } uint8_t t = vtype(obj); bool is_promise = (t == T_PROMISE); - if (t == T_OBJ && is_proxy(js, obj)) { + if (t == T_OBJ && is_proxy(obj)) { *out = proxy_get(js, obj, key, key_len); return true; } if (t == T_STR || t == T_NUM || t == T_BOOL) { - if (t == T_STR && key_len == 6 && memcmp(key, "length", 6) == 0) { + if (t == T_STR && is_length_key(key, key_len)) { ant_offset_t byte_len = 0; ant_offset_t str_off = vstr(js, obj, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); *out = tov((double)utf16_strlen(str_data, byte_len)); return true; } ant_value_t boxed = mkobj(js, 0); - js_set_slot(js, js_as_obj(boxed), SLOT_PRIMITIVE, obj); + js_set_slot(js_as_obj(boxed), SLOT_PRIMITIVE, obj); obj = boxed; t = T_OBJ; } @@ -11853,7 +12514,7 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t ant_value_t promise_proto = get_ctor_proto(js, "Promise", 7); if (vtype(promise_proto) != T_UNDEF && vtype(promise_proto) != T_NULL) { off = lkp(js, promise_proto, key, key_len); - if (off != 0) { *out = resolveprop(js, mkval(T_PROP, off)); return true; } + if (off != 0) { *out = propref_load(js, off); return true; } } } @@ -11865,15 +12526,15 @@ static bool js_try_get(ant_t *js, ant_value_t obj, const char *key, ant_value_t return false; } - descriptor_entry_t *desc = lookup_descriptor(js, (ant_offset_t)vdata(js_as_obj(obj)), key, key_len); - if (desc && desc->has_getter) { + const ant_shape_prop_t *prop_meta = prop_shape_meta(js, off); + if (prop_meta && prop_meta->has_getter) { ant_value_t accessor_result; if (try_accessor_getter(js, obj, key, key_len, &accessor_result)) { *out = accessor_result; return true; } } - *out = resolveprop(js, mkval(T_PROP, off)); + *out = propref_load(js, off); return true; } @@ -11886,7 +12547,7 @@ ant_value_t js_get(ant_t *js, ant_value_t obj, const char *key) { ant_value_t js_getprop_proto(ant_t *js, ant_value_t obj, const char *key) { size_t key_len = strlen(key); ant_offset_t off = lkp_proto(js, obj, key, key_len); - return off == 0 ? js_mkundef() : resolveprop(js, mkval(T_PROP, off)); + return off == 0 ? js_mkundef() : propref_load(js, off); } ant_value_t js_getprop_fallback(ant_t *js, ant_value_t obj, const char *name) { @@ -11902,31 +12563,54 @@ ant_value_t js_getprop_super(ant_t *js, ant_value_t super_obj, ant_value_t recei if (!is_object_type(super_obj)) return js_mkundef(); size_t key_len = strlen(name); - if (is_proxy(js, super_obj)) return proxy_get(js, super_obj, name, key_len); + if (is_proxy(super_obj)) return proxy_get(js, super_obj, name, key_len); const char *key_intern = intern_string(name, key_len); if (!key_intern) return js_mkundef(); - ant_value_t cur = js_as_obj(super_obj); - for (int i = 0; i < MAX_PROTO_CHAIN_DEPTH; i++) { - ant_offset_t cur_off = (ant_offset_t)vdata(cur); - descriptor_entry_t *desc = lookup_descriptor(js, cur_off, name, key_len); - if (desc) { - if (desc->has_getter) { - ant_value_t getter = desc->getter; - if (vtype(getter) == T_FUNC || vtype(getter) == T_CFUNC) - return sv_vm_call(js->vm, js, getter, receiver, NULL, 0, NULL, false); - return js_mkundef(); + ant_value_t cur = super_obj; + proto_overflow_guard_t guard; + proto_overflow_guard_init(&guard); + while (is_object_type(cur)) { + ant_value_t cur_obj = js_as_obj(cur); + ant_object_t *cur_ptr = js_obj_ptr(cur_obj); + bool handled = false; + + if (cur_ptr && cur_ptr->shape) { + int32_t slot = ant_shape_lookup_interned(cur_ptr->shape, key_intern); + if (slot >= 0) { + const ant_shape_prop_t *prop = ant_shape_prop_at(cur_ptr->shape, (uint32_t)slot); + if (prop && prop->has_getter) { + ant_value_t getter = prop->getter; + if (vtype(getter) == T_FUNC || vtype(getter) == T_CFUNC) + return sv_vm_call(js->vm, js, getter, receiver, NULL, 0, NULL, false); + return js_mkundef(); + } + if (prop && prop->has_setter) return js_mkundef(); + handled = true; + } + } + + if (!handled && cur_ptr && cur_ptr->is_exotic) { + descriptor_entry_t *desc = lookup_descriptor(cur_obj, name, key_len); + if (desc) { + if (desc->has_getter) { + ant_value_t getter = desc->getter; + if (vtype(getter) == T_FUNC || vtype(getter) == T_CFUNC) + return sv_vm_call(js->vm, js, getter, receiver, NULL, 0, NULL, false); + return js_mkundef(); + } + if (desc->has_setter) return js_mkundef(); } - if (desc->has_setter) return js_mkundef(); } - ant_offset_t prop_off = lkp_interned(js, cur, key_intern, key_len); - if (prop_off != 0) return resolveprop(js, mkval(T_PROP, prop_off)); + ant_offset_t prop_off = lkp_interned(js, cur_obj, key_intern, key_len); + if (prop_off != 0) return propref_load(js, prop_off); - ant_value_t proto = get_proto(js, cur); + ant_value_t proto = get_proto(js, cur_obj); if (!is_object_type(proto)) break; - cur = js_as_obj(proto); + cur = proto; + if (proto_overflow_guard_hit_cycle(js, &guard, cur)) break; } return js_mkundef(); @@ -11949,284 +12633,30 @@ bool js_iter(ant_t *js, ant_value_t iterable, bool (*callback)(ant_t *js, ant_va } char *js_getstr(ant_t *js, ant_value_t value, size_t *len) { + (void)js; if (vtype(value) != T_STR) return NULL; ant_offset_t n, off = vstr(js, value, &n); if (len != NULL) *len = n; - return (char *) &js->mem[off]; + return (char *)(uintptr_t)off; } void js_merge_obj(ant_t *js, ant_value_t dst, ant_value_t src) { if (vtype(dst) != T_OBJ || vtype(src) != T_OBJ) return; - ant_offset_t next = loadoff(js, (ant_offset_t) vdata(src)) & ~(3U | FLAGMASK); - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (is_slot_prop(header)) { next = next_prop(header); continue; } - - ant_offset_t koff = loadoff(js, next + (ant_offset_t) sizeof(next)); - ant_value_t val = loadval(js, next + (ant_offset_t) (sizeof(next) + sizeof(koff))); - - js_setprop(js, dst, mkval(T_STR, koff), val); - next = next_prop(header); - } -} - -#define UTARRAY_EACH(arr, type, var) \ - if (arr) for (type *var = (type *)utarray_front(arr), *_end = var + utarray_len(arr); var < _end; var++) - -#define REHASH_REGISTRY(registry, entry, tmp, new_reg, key_field, key_size, body) \ -for (typeof(registry) new_reg = NULL, *_once = NULL; !_once; _once = (void*)1, registry = new_reg) \ - HASH_ITER(hh, registry, entry, tmp) { \ - HASH_DEL(registry, entry); \ - body; \ - HASH_ADD(hh, new_reg, key_field, key_size, entry); \ -} - -typedef ant_offset_t (*gc_fwd_off_fn)(void *ctx, ant_offset_t old); -typedef ant_value_t (*gc_fwd_val_fn)(void *ctx, ant_value_t old); -typedef void (*gc_off_op_t)(void *cb_ctx, ant_offset_t *off); -typedef void (*gc_val_op_t)(void *cb_ctx, ant_value_t *val); - -typedef struct { - gc_fwd_off_fn fwd_off; - gc_fwd_val_fn fwd_val; - gc_fwd_off_fn weak_off; - void *ctx; - ant_t *js; -} gc_cb_ctx_t; - -static inline void gc_reserve_off_cb(void *cb_ctx, ant_offset_t *off) { - gc_cb_ctx_t *c = cb_ctx; - if (*off) (void)c->fwd_off(c->ctx, *off); -} - -static inline void gc_reserve_val_cb(void *cb_ctx, ant_value_t *val) { - gc_cb_ctx_t *c = cb_ctx; - (void)c->fwd_val(c->ctx, *val); -} - -static inline void gc_update_off_cb(void *cb_ctx, ant_offset_t *off) { - gc_cb_ctx_t *c = cb_ctx; - if (*off) *off = c->fwd_off(c->ctx, *off); -} - -static inline void gc_update_val_cb(void *cb_ctx, ant_value_t *val) { - gc_cb_ctx_t *c = cb_ctx; - *val = c->fwd_val(c->ctx, *val); -} - -static inline ant_offset_t gc_weak_off_cb(void *cb_ctx, ant_offset_t old) { - gc_cb_ctx_t *c = cb_ctx; - return c->weak_off(c->ctx, old); -} - -void js_gc_visit_frame_funcs(ant_t *js, void (*visitor)(void *, sv_func_t *), void *ctx) { - sv_vm_visit_frame_funcs(js->vm, visitor, ctx); - for (coroutine_t *coro = pending_coroutines.head; coro; coro = coro->next) { - if (coro->sv_vm) sv_vm_visit_frame_funcs(coro->sv_vm, visitor, ctx); - } -} - -static void gc_roots_common(gc_off_op_t op_off, gc_val_op_t op_val, gc_cb_ctx_t *c) { - if (rt && rt->js == c->js) - op_val(c, &rt->ant_obj); - - for (coroutine_t *coro = pending_coroutines.head; coro; coro = coro->next) { - op_val(c, &coro->this_val); - op_val(c, &coro->super_val); - op_val(c, &coro->new_target); - op_val(c, &coro->awaited_promise); - op_val(c, &coro->result); - op_val(c, &coro->async_func); - op_val(c, &coro->yield_value); - op_val(c, &coro->async_promise); - } - - js_esm_gc_roots(op_val, c); - - for (ant_module_t *ctx = c->js->module; ctx; ctx = ctx->prev) { - op_val(c, &ctx->module_ns); - op_val(c, &ctx->import_meta); - op_val(c, &ctx->prev_import_meta_prop); - } + ant_value_t as_src = js_as_obj(src); + ant_object_t *src_obj = js_obj_ptr(as_src); + if (!src_obj || !src_obj->shape) return; - timer_gc_update_roots(op_val, c); - ffi_gc_update_roots(op_val, c); - fetch_gc_update_roots(op_val, c); - fs_gc_update_roots(op_val, c); - child_process_gc_update_roots(op_val, c); - readline_gc_update_roots(op_val, c); - process_gc_update_roots(op_val, c); - navigator_gc_update_roots(op_val, c); - server_gc_update_roots(op_val, c); - events_gc_update_roots(op_val, c); - lmdb_gc_update_roots(op_val, c); - symbol_gc_update_roots(op_val, c); - - op_val(c, &c->js->global); - op_val(c, &c->js->object); - op_val(c, &c->js->this_val); - op_val(c, &c->js->new_target); - op_val(c, &c->js->current_func); - op_val(c, &c->js->thrown_value); - op_val(c, &c->js->length_str); - - sv_vm_gc_roots(c->js->vm, op_val, c); - sv_vm_gc_roots_pending(op_val, c); - - for (ant_handle_t i = 0; i < c->js->gc_roots_len; i++) op_val(c, &c->js->gc_roots[i]); -} + uint32_t count = ant_shape_count(src_obj->shape); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(src_obj->shape, i); + if (!prop || prop->type != ANT_SHAPE_KEY_STRING) continue; -void js_gc_reserve_roots(GC_RESERVE_ARGS) { - #define RSV_OFF(x) ((x) ? (void)fwd_off(ctx, x) : (void)0) - #define RSV_VAL(x) (void)fwd_val(ctx, x) - - gc_cb_ctx_t cb_ctx = { fwd_off, fwd_val, NULL, ctx, js }; - gc_roots_common(gc_reserve_off_cb, gc_reserve_val_cb, &cb_ctx); - collections_gc_reserve_roots(gc_reserve_val_cb, &cb_ctx); - - promise_data_entry_t *pd, *pd_tmp; - HASH_ITER(hh, promise_registry, pd, pd_tmp) { - bool can_collect = (pd->state != 0) - && (utarray_len(pd->handlers) == 0) - && !pd->processing; - - if (can_collect) continue; - RSV_OFF(pd->obj_offset); - RSV_VAL(pd->value); - - UTARRAY_EACH(pd->handlers, promise_handler_t, h) { - RSV_VAL(h->onFulfilled); - RSV_VAL(h->onRejected); - RSV_VAL(h->nextPromise); - } - } - - proxy_data_t *proxy, *proxy_tmp; - HASH_ITER(hh, proxy_registry, proxy, proxy_tmp) { - RSV_VAL(proxy->target); - RSV_VAL(proxy->handler); - } - - descriptor_entry_t *desc, *desc_tmp; - HASH_ITER(hh, desc_registry, desc, desc_tmp) { - if (desc->has_getter) RSV_VAL(desc->getter); - if (desc->has_setter) RSV_VAL(desc->setter); + const char *key = prop->key.interned; + ant_value_t val = (i < src_obj->prop_count) ? ant_object_prop_get_unchecked(src_obj, i) : js_mkundef(); + js_setprop(js, dst, js_mkstr(js, key, strlen(key)), val); } - - // accessor registry is a weak reference - // objects only survive if reachable from other roots - (void)accessor_registry; - - #undef RSV_OFF - #undef RSV_VAL -} - -void js_gc_update_roots(GC_UPDATE_ARGS) { - #define FWD_OFF(x) ((x) ? ((x) = fwd_off(ctx, x)) : 0) - #define FWD_VAL(x) ((x) = fwd_val(ctx, x)) - #define IS_UNREACHABLE(old, new) ((new) == (old) && (old) != 0 && (old) < js->brk) - - gc_cb_ctx_t cb_ctx = { fwd_off, fwd_val, weak_off, ctx, js }; - gc_roots_common(gc_update_off_cb, gc_update_val_cb, &cb_ctx); - - promise_data_entry_t *pd, *pd_tmp; - promise_data_entry_t *new_unhandled = NULL; - - for ( - promise_data_entry_t *new_promise_registry = NULL, *_once = NULL; !_once; _once = (void*)1, - promise_registry = new_promise_registry, unhandled_rejections = new_unhandled - ) - HASH_ITER(hh, promise_registry, pd, pd_tmp) { - HASH_DEL(promise_registry, pd); - promise_data_entry_t *in_unhandled = NULL; - - HASH_FIND( - hh_unhandled, unhandled_rejections, - &pd->promise_id, sizeof(uint32_t), in_unhandled - ); - - if (in_unhandled) HASH_DELETE(hh_unhandled, unhandled_rejections, pd); - ant_offset_t new_off = weak_off(ctx, pd->obj_offset); - - if (new_off == (ant_offset_t)~0 && !pd->processing) { - utarray_free(pd->handlers); - free(pd); continue; - } - - bool can_collect = (pd->state != 0) - && (utarray_len(pd->handlers) == 0) - && !pd->processing; - - if (can_collect) { - utarray_free(pd->handlers); - free(pd); continue; - } - - pd->obj_offset = new_off; - FWD_VAL(pd->value); - - UTARRAY_EACH(pd->handlers, promise_handler_t, h) { - FWD_VAL(h->onFulfilled); - FWD_VAL(h->onRejected); - FWD_VAL(h->nextPromise); - } - - HASH_ADD(hh, new_promise_registry, promise_id, sizeof(uint32_t), pd); - if (in_unhandled) HASH_ADD(hh_unhandled, new_unhandled, promise_id, sizeof(uint32_t), pd); - } - - - proxy_data_t *proxy, *proxy_tmp; - for (proxy_data_t *new_proxy_registry = NULL, *_once = NULL; !_once; _once = (void*)1, proxy_registry = new_proxy_registry) - HASH_ITER(hh, proxy_registry, proxy, proxy_tmp) { - HASH_DEL(proxy_registry, proxy); - ant_offset_t new_off = weak_off(ctx, proxy->obj_offset); - if (new_off == (ant_offset_t)~0) { free(proxy); continue; } - proxy->obj_offset = new_off; - FWD_VAL(proxy->target); FWD_VAL(proxy->handler); - HASH_ADD(hh, new_proxy_registry, obj_offset, sizeof(ant_offset_t), proxy); - } - - dynamic_accessors_t *acc, *acc_tmp; - for (dynamic_accessors_t *new_acc_registry = NULL, *_once = NULL; !_once; _once = (void*)1, accessor_registry = new_acc_registry) - HASH_ITER(hh, accessor_registry, acc, acc_tmp) { - HASH_DEL(accessor_registry, acc); - ant_offset_t new_off = weak_off(ctx, acc->obj_offset); - if (new_off == (ant_offset_t)~0) { free(acc); continue; } - acc->obj_offset = new_off; - HASH_ADD(hh, new_acc_registry, obj_offset, sizeof(ant_offset_t), acc); - } - - descriptor_entry_t *desc, *desc_tmp; - for (descriptor_entry_t *new_desc_registry = NULL, *_once = NULL; !_once; _once = (void*)1, desc_registry = new_desc_registry) - HASH_ITER(hh, desc_registry, desc, desc_tmp) { - HASH_DEL(desc_registry, desc); - ant_offset_t new_off = weak_off(ctx, (ant_offset_t)(desc->key >> 32)); - if (new_off == (ant_offset_t)~0) { free(desc); continue; } - if (desc->has_getter) FWD_VAL(desc->getter); - if (desc->has_setter) FWD_VAL(desc->setter); - if (desc->prop_name) { - desc->key = ((uint64_t)new_off << 32) | (uint32_t)(desc->key & 0xFFFFFFFF); - } else { - ant_offset_t new_sym = fwd_off(ctx, desc->sym_off); - desc->sym_off = new_sym; - desc->key = make_sym_desc_key(new_off, new_sym); - } - desc->obj_off = new_off; - HASH_ADD(hh, new_desc_registry, key, sizeof(uint64_t), desc); - } - - intern_prop_cache_gen++; - collections_gc_update_roots(gc_weak_off_cb, gc_update_val_cb, &cb_ctx); - regex_gc_update_roots(gc_weak_off_cb, gc_update_val_cb, &cb_ctx); - - #undef FWD_OFF - #undef FWD_VAL } -#undef UTARRAY_EACH -#undef REHASH_REGISTRY - bool js_chkargs(ant_value_t *args, int nargs, const char *spec) { int i = 0, ok = 1; for (; ok && i < nargs && spec[i]; i++) { @@ -12306,7 +12736,7 @@ ant_value_t sv_call_native( sv_closure_t *closure = js_func_closure(func); ant_value_t func_obj = closure->func_obj; - ant_value_t cfunc_slot = get_slot(js, func_obj, SLOT_CFUNC); + ant_value_t cfunc_slot = get_slot(func_obj, SLOT_CFUNC); if (vtype(cfunc_slot) == T_CFUNC) { ant_value_t resolve_this = (vtype(closure->bound_this) != T_UNDEF) ? closure->bound_this : this_val; int final_nargs = nargs; @@ -12328,7 +12758,7 @@ ant_value_t sv_call_native( return res; } - ant_value_t builtin_slot = get_slot(js, func_obj, SLOT_BUILTIN); + ant_value_t builtin_slot = get_slot(func_obj, SLOT_BUILTIN); if (vtype(builtin_slot) == T_NUM && (int)tod(builtin_slot) == BUILTIN_OBJECT) { ant_value_t saved_this = js->this_val; js->this_val = this_val; @@ -12342,149 +12772,139 @@ ant_value_t sv_call_native( } ant_iter_t js_prop_iter_begin(ant_t *js, ant_value_t obj) { - ant_iter_t iter = {.ctx = js, .off = 0}; - + typedef struct { + ant_t *js; + ant_object_t *obj; + uint32_t index; + } prop_iter_ctx_t; + + ant_iter_t iter = {.ctx = NULL, .off = 0}; uint8_t t = vtype(obj); - if (t == T_OBJ || t == T_ARR || t == T_FUNC) { - iter.off = (ant_offset_t)vdata(js_as_obj(obj)); - } - + if (t != T_OBJ && t != T_ARR && t != T_FUNC) return iter; + + prop_iter_ctx_t *ctx = calloc(1, sizeof(*ctx)); + if (!ctx) return iter; + ctx->js = js; + ctx->obj = js_obj_ptr(js_as_obj(obj)); + ctx->index = 0; + if (!ctx->obj || !ctx->obj->shape) { + free(ctx); + return iter; + } + iter.ctx = ctx; return iter; } bool js_prop_iter_next(ant_iter_t *iter, const char **key, size_t *key_len, ant_value_t *value) { + typedef struct { + ant_t *js; + ant_object_t *obj; + uint32_t index; + } prop_iter_ctx_t; + if (!iter || !iter->ctx) return false; - - ant_t *js = (ant_t *)iter->ctx; - ant_offset_t next = loadoff(js, iter->off) & ~(3U | FLAGMASK); - - while (next < js->brk && next != 0) { - ant_offset_t header = loadoff(js, next); - if (!is_slot_prop(header) && !is_sym_key_prop(js, next)) break; - next = next_prop(header); - } - - if (next >= js->brk || next == 0) return false; - iter->off = next; - - ant_offset_t koff = loadoff(js, next + (ant_offset_t)sizeof(ant_offset_t)); - ant_value_t val = loadval(js, next + (ant_offset_t)(sizeof(ant_offset_t) * 2)); - - if (key) { - ant_offset_t klen = offtolen(loadoff(js, koff)); - *key = (const char *)&js->mem[koff + sizeof(ant_offset_t)]; - if (key_len) *key_len = klen; + prop_iter_ctx_t *ctx = (prop_iter_ctx_t *)iter->ctx; + ant_object_t *obj = ctx->obj; + if (!obj || !obj->shape) return false; + + uint32_t count = ant_shape_count(obj->shape); + while (ctx->index < count) { + uint32_t i = ctx->index++; + const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); + if (!prop) continue; + if (prop->type == ANT_SHAPE_KEY_SYMBOL) continue; + if (i >= obj->prop_count) continue; + + if (key) { + *key = prop->key.interned; + if (key_len) *key_len = strlen(prop->key.interned); + } + if (value) *value = ant_object_prop_get_unchecked(obj, i); + iter->off = i + 1; + return true; } - if (value) *value = val; - - return true; + + return false; } void js_prop_iter_end(ant_iter_t *iter) { - if (iter) { iter->off = 0; iter->ctx = NULL; } + if (!iter) return; + free(iter->ctx); + iter->off = 0; + iter->ctx = NULL; } ant_value_t js_mkpromise(ant_t *js) { return mkpromise(js); } void js_resolve_promise(ant_t *js, ant_value_t promise, ant_value_t value) { resolve_promise(js, promise, value); } void js_reject_promise(ant_t *js, ant_value_t promise, ant_value_t value) { reject_promise(js, promise, value); } -void js_check_unhandled_rejections(ant_t *js) { - promise_data_entry_t *pd, *tmp; - - HASH_ITER(hh_unhandled, unhandled_rejections, pd, tmp) { - if (pd->has_rejection_handler) { - HASH_DELETE(hh_unhandled, unhandled_rejections, pd); continue; - } - - if (pd->trigger_pid != 0) { - promise_data_entry_t *parent; - HASH_FIND(hh, promise_registry, &pd->trigger_pid, sizeof(uint32_t), parent); - if (parent && parent->has_rejection_handler) { - HASH_DELETE(hh_unhandled, unhandled_rejections, pd); continue; - } - } - - if (js->fatal_error) { - js->thrown_exists = true; - js->thrown_value = pd->value; - print_uncaught_throw(js); - js_destroy(js); exit(1); - } - - print_unhandled_promise_rejection(js, pd->value); - pd->has_rejection_handler = true; - HASH_DELETE(hh_unhandled, unhandled_rejections, pd); +static void check_unhandled_rejections_list(ant_t *js, ant_object_t *list) { +for (ant_object_t *obj = list; obj; obj = obj->next) { + ant_promise_state_t *pd = obj->promise_state; + if (!pd) continue; + if (pd->state != 2) continue; + if (pd->has_rejection_handler || pd->unhandled_reported) continue; + + if (vtype(pd->trigger_parent) == T_PROMISE) { + ant_promise_state_t *parent = get_promise_data(js, pd->trigger_parent, false); + if (parent && parent->has_rejection_handler) continue; } -} -bool js_is_slot_prop(ant_offset_t header) { return is_slot_prop(header); } -ant_offset_t js_next_prop(ant_offset_t header) { return next_prop(header); } -ant_offset_t js_loadoff(ant_t *js, ant_offset_t off) { return loadoff(js, off); } + if (js->fatal_error) { + js->thrown_exists = true; + js->thrown_value = pd->value; + print_uncaught_throw(js); + js_destroy(js); exit(1); + } + + print_unhandled_promise_rejection(js, pd->value); + pd->unhandled_reported = true; +}} + +void js_check_unhandled_rejections(ant_t *js) { + check_unhandled_rejections_list(js, js->objects); + check_unhandled_rejections_list(js, js->objects_old); + check_unhandled_rejections_list(js, js->permanent_objects); +} -void js_set_getter(ant_t *js, ant_value_t obj, js_getter_fn getter) { +void js_set_getter(ant_value_t obj, js_getter_fn getter) { if (!is_object_type(obj)) return; if (vtype(obj) != T_OBJ) obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry) { - entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); - if (!entry) return; - entry->obj_offset = obj_off; - entry->getter = NULL; entry->setter = NULL; - entry->deleter = NULL; entry->keys = NULL; - HASH_ADD(hh, accessor_registry, obj_offset, sizeof(ant_offset_t), entry); - } - entry->getter = getter; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return; + ptr->is_exotic = 1; + ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr); + if (!ops) return; + ops->getter = getter; } -void js_set_setter(ant_t *js, ant_value_t obj, js_setter_fn setter) { +void js_set_setter(ant_value_t obj, js_setter_fn setter) { if (!is_object_type(obj)) return; if (vtype(obj) != T_OBJ) obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry) { - entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); - if (!entry) return; - entry->obj_offset = obj_off; - entry->getter = NULL; entry->setter = NULL; - entry->deleter = NULL; entry->keys = NULL; - HASH_ADD(hh, accessor_registry, obj_offset, sizeof(ant_offset_t), entry); - } - entry->setter = setter; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return; + ptr->is_exotic = 1; + ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr); + if (!ops) return; + ops->setter = setter; } -void js_set_deleter(ant_t *js, ant_value_t obj, js_deleter_fn deleter) { +void js_set_deleter(ant_value_t obj, js_deleter_fn deleter) { if (!is_object_type(obj)) return; if (vtype(obj) != T_OBJ) obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry) { - entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); - if (!entry) return; - entry->obj_offset = obj_off; - entry->getter = NULL; entry->setter = NULL; - entry->deleter = NULL; entry->keys = NULL; - HASH_ADD(hh, accessor_registry, obj_offset, sizeof(ant_offset_t), entry); - } - entry->deleter = deleter; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return; + ptr->is_exotic = 1; + ant_exotic_ops_t *ops = obj_ensure_exotic_ops(ptr); + if (!ops) return; + ops->deleter = deleter; } -void js_set_keys(ant_t *js, ant_value_t obj, js_keys_fn keys) { +void js_set_keys(ant_value_t obj, js_keys_fn keys) { if (!is_object_type(obj)) return; if (vtype(obj) != T_OBJ) obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - dynamic_accessors_t *entry = NULL; - HASH_FIND(hh, accessor_registry, &obj_off, sizeof(ant_offset_t), entry); - if (!entry) { - entry = (dynamic_accessors_t *)malloc(sizeof(dynamic_accessors_t)); - if (!entry) return; - entry->obj_offset = obj_off; - entry->getter = NULL; entry->setter = NULL; - entry->deleter = NULL; entry->keys = NULL; - HASH_ADD(hh, accessor_registry, obj_offset, sizeof(ant_offset_t), entry); - } - entry->keys = keys; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr) return; + ptr->is_exotic = 1; + ptr->exotic_keys = keys; } diff --git a/src/cli/pkg.c b/src/cli/pkg.c index c971b38..5bbbd4e 100644 --- a/src/cli/pkg.c +++ b/src/cli/pkg.c @@ -1259,21 +1259,18 @@ int pkg_cmd_exec(int argc, char **argv) { printf("Options:\n"); printf(" --ant Run with ant instead of node\n\n"); printf("Available commands:\n"); - + int count = pkg_list_bins("node_modules", NULL, NULL); - if (count < 0) { - printf(" (no binaries found - run 'ant install' first)\n"); - } else if (count == 0) { - printf(" (no binaries installed)\n"); - } else { - pkg_list_bins("node_modules", print_bin_name, NULL); - } + if (count < 0) printf(" (no binaries found - run 'ant install' first)\n"); + else if (count == 0) printf(" (no binaries installed)\n"); + else pkg_list_bins("node_modules", print_bin_name, NULL); + return EXIT_SUCCESS; } - + bool use_ant = false; int cmd_idx = 1; - + if (strcmp(argv[1], "--ant") == 0) { use_ant = true; cmd_idx = 2; @@ -1282,12 +1279,10 @@ int pkg_cmd_exec(int argc, char **argv) { return EXIT_FAILURE; } } - - const char *cmd_name = argv[cmd_idx]; - char bin_path[4096]; - + + const char *cmd_name = argv[cmd_idx]; char bin_path[4096]; int path_len = pkg_get_bin_path("node_modules", cmd_name, bin_path, sizeof(bin_path)); - + if (path_len < 0) { const char *global_dir = get_global_dir(); if (global_dir[0]) { @@ -1296,31 +1291,33 @@ int pkg_cmd_exec(int argc, char **argv) { path_len = pkg_get_bin_path(global_nm, cmd_name, bin_path, sizeof(bin_path)); } } - + if (path_len < 0) { progress_t progress; - - if (!pkg_verbose) { + bool show_progress = !pkg_verbose; + + if (show_progress) { char msg[256]; snprintf(msg, sizeof(msg), "🔍 Resolving %s", cmd_name); progress_start(&progress, msg); } - - pkg_options_t opts = { - .progress_callback = pkg_verbose ? NULL : progress_callback, - .user_data = pkg_verbose ? NULL : &progress, - .verbose = pkg_verbose + + pkg_options_t opts = { + .progress_callback = show_progress ? progress_callback : NULL, + .user_data = show_progress ? &progress : NULL, + .verbose = pkg_verbose }; + pkg_context_t *ctx = pkg_init(&opts); if (!ctx) { - if (!pkg_verbose) progress_stop(&progress); + if (show_progress) progress_stop(&progress); fprintf(stderr, "Error: Failed to initialize package manager\n"); return EXIT_FAILURE; } - + pkg_error_t err = pkg_exec_temp(ctx, cmd_name, bin_path, sizeof(bin_path)); - if (!pkg_verbose) progress_stop(&progress); - + if (show_progress) progress_stop(&progress); + if (err != PKG_OK) { const char *err_msg = pkg_error_string(ctx); if (err_msg && err_msg[0]) { @@ -1333,28 +1330,31 @@ int pkg_cmd_exec(int argc, char **argv) { } pkg_free(ctx); } - - const char *runtime = use_ant ? "ant" : "node"; + int arg_offset = cmd_idx + 1; - int new_argc = argc - arg_offset + 2; + int extra = use_ant ? 2 : 1; + int new_argc = argc - arg_offset + extra; char **exec_argv = try_oom(sizeof(char*) * (new_argc + 1)); if (!exec_argv) { fprintf(stderr, "Error: out of memory\n"); return EXIT_FAILURE; } + + int idx = 0; + if (use_ant) exec_argv[idx++] = (char *)"ant"; + exec_argv[idx++] = bin_path; - exec_argv[0] = (char *)runtime; - exec_argv[1] = bin_path; - for (int i = arg_offset; i < argc; i++) { - exec_argv[i - arg_offset + 2] = argv[i]; - } - exec_argv[new_argc] = NULL; - - execvp(runtime, exec_argv); - + for (int i = arg_offset; i < argc; i++) exec_argv[idx++] = argv[i]; + exec_argv[idx] = NULL; + + const char *cmd = use_ant ? "ant" : bin_path; + execvp(cmd, exec_argv); free(exec_argv); - fprintf(stderr, "Error: failed to execute '%s %s' - is %s installed?\n", runtime, bin_path, runtime); + + if (use_ant) fprintf(stderr, "Error: failed to execute 'ant %s' - is ant installed?\n", bin_path); + else fprintf(stderr, "Error: failed to execute '%s'\n", bin_path); + return EXIT_FAILURE; } diff --git a/src/core/ant.ts b/src/core/ant.ts index 40d8f8d..8f42c27 100644 --- a/src/core/ant.ts +++ b/src/core/ant.ts @@ -9,7 +9,6 @@ Ant.typeof = function (t) { const types = { T_OBJ: 'object', - T_PROP: 'prop', T_STR: 'string', T_ARR: 'array', T_FUNC: 'function', diff --git a/src/descriptors.c b/src/descriptors.c index 6140450..99b1b11 100644 --- a/src/descriptors.c +++ b/src/descriptors.c @@ -1,181 +1,409 @@ +#include "gc.h" +#include "utils.h" +#include "shapes.h" +#include "internal.h" #include "descriptors.h" #include "silver/engine.h" -#include "arena.h" -#include "utils.h" + +#include +#include descriptor_entry_t *desc_registry = NULL; static descriptor_entry_t arr_length_desc = { - .key = 0, .obj_off = 0, - .prop_name = "length", .prop_len = 6, - .writable = true, .enumerable = false, .configurable = false, - .has_getter = false, .has_setter = false, - .getter = 0, .setter = 0, + .key = 0, + .obj_off = 0, + .prop_name = "length", + .prop_len = 6, + .writable = true, + .enumerable = false, + .configurable = false, + .has_getter = false, + .has_setter = false, + .getter = 0, + .setter = 0, }; -uint64_t make_desc_key(ant_offset_t obj_off, const char *key, size_t klen) { - uint32_t key_hash = (uint32_t)hash_key(key, klen) & 0x7FFFFFFFu; - return ((uint64_t)obj_off << 32) | key_hash; +static inline bool is_canonical_desc_obj(ant_value_t obj) { + return vtype(obj) == T_OBJ; } -uint64_t make_sym_desc_key(ant_offset_t obj_off, ant_offset_t sym_off) { - return ((uint64_t)obj_off << 32) | ((uint32_t)sym_off | 0x80000000u); +static inline bool is_exotic_desc_obj(ant_value_t obj) { + ant_object_t *ptr = js_obj_ptr(obj); + return ptr && ptr->is_exotic; } -descriptor_entry_t *lookup_descriptor(ant_t *js, ant_offset_t obj_off, const char *key, size_t klen) { - if (klen == 6 - && memcmp(key, "length", 6) == 0 - && is_arr_off(js, obj_off) - ) return &arr_length_desc; +static inline bool desc_registry_allowed(ant_value_t obj) { + if (is_exotic_desc_obj(obj)) return true; +#ifndef NDEBUG + assert(false && "desc_registry is exotic-only; ordinary objects must use shape metadata"); +#endif + return false; +} + +static inline uint8_t attrs_from_flags(int flags, bool include_writable) { + uint8_t attrs = 0; + if (include_writable && (flags & JS_DESC_W)) attrs |= ANT_PROP_ATTR_WRITABLE; + if (flags & JS_DESC_E) attrs |= ANT_PROP_ATTR_ENUMERABLE; + if (flags & JS_DESC_C) attrs |= ANT_PROP_ATTR_CONFIGURABLE; + return attrs; +} +uint64_t make_desc_key(ant_value_t obj, const char *key, size_t klen) { + assert(is_canonical_desc_obj(obj) && "descriptor APIs require canonical js_as_obj(...) value"); + if (!is_canonical_desc_obj(obj)) return hash_key(key, klen); + uintptr_t obj_off = (uintptr_t)vdata(obj); + uint64_t key_hash = hash_key(key, klen); + uint64_t h = (uint64_t)obj_off ^ ((uint64_t)obj_off >> 33); + h ^= key_hash + 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2); + return h; +} + +uint64_t make_sym_desc_key(ant_value_t obj, ant_offset_t sym_off) { + assert(is_canonical_desc_obj(obj) && "descriptor APIs require canonical js_as_obj(...) value"); + if (!is_canonical_desc_obj(obj)) return ((uint64_t)sym_off << 1) | 1u; + uintptr_t obj_off = (uintptr_t)vdata(obj); + uint64_t h = (uint64_t)obj_off ^ ((uint64_t)obj_off >> 33); + h ^= ((uint64_t)sym_off << 1) | 1u; + h += 0x9e3779b97f4a7c15ULL + (h << 6) + (h >> 2); + return h; +} + +static descriptor_entry_t *registry_lookup_desc(ant_value_t obj, const char *key, size_t klen) { descriptor_entry_t *entry = NULL; - uint64_t desc_key = make_desc_key(obj_off, key, klen); + uint64_t desc_key = make_desc_key(obj, key, klen); HASH_FIND(hh, desc_registry, &desc_key, sizeof(uint64_t), entry); - return entry; } -descriptor_entry_t *lookup_sym_descriptor(ant_offset_t obj_off, ant_offset_t sym_off) { +static descriptor_entry_t *registry_lookup_sym_desc(ant_value_t obj, ant_offset_t sym_off) { descriptor_entry_t *entry = NULL; - uint64_t k = make_sym_desc_key(obj_off, sym_off); + uint64_t k = make_sym_desc_key(obj, sym_off); HASH_FIND(hh, desc_registry, &k, sizeof(uint64_t), entry); return entry; } +descriptor_entry_t *lookup_descriptor(ant_value_t obj, const char *key, size_t klen) { + assert(is_canonical_desc_obj(obj) && "lookup_descriptor expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return NULL; + + ant_object_t *ptr = js_obj_ptr(obj); + if (klen == 6 && memcmp(key, "length", 6) == 0 && ptr && ptr->type_tag == T_ARR) + return &arr_length_desc; + + if (!is_exotic_desc_obj(obj)) return NULL; + return registry_lookup_desc(obj, key, klen); +} + +descriptor_entry_t *lookup_sym_descriptor(ant_value_t obj, ant_offset_t sym_off) { + assert(is_canonical_desc_obj(obj) && "lookup_sym_descriptor expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return NULL; + + if (!is_exotic_desc_obj(obj)) return NULL; + return registry_lookup_sym_desc(obj, sym_off); +} + static descriptor_entry_t *get_or_create_desc(ant_t *js, ant_value_t obj, const char *key, size_t klen) { - if (vtype(obj) != T_OBJ && vtype(obj) != T_FUNC && vtype(obj) != T_ARR) return NULL; - obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - uint64_t desc_key = make_desc_key(obj_off, key, klen); + if (!desc_registry_allowed(obj)) return NULL; + descriptor_entry_t *entry = registry_lookup_desc(obj, key, klen); + if (entry) return entry; - descriptor_entry_t *entry = NULL; - HASH_FIND(hh, desc_registry, &desc_key, sizeof(uint64_t), entry); - if (!entry) { - entry = (descriptor_entry_t *)ant_calloc(sizeof(descriptor_entry_t) + klen + 1); - if (!entry) return NULL; - - entry->key = desc_key; - entry->obj_off = obj_off; - entry->prop_name = (char *)(entry + 1); - memcpy(entry->prop_name, key, klen); - entry->prop_name[klen] = '\0'; - entry->prop_len = klen; - entry->writable = true; - entry->enumerable = true; - entry->configurable = true; - entry->has_getter = false; - entry->has_setter = false; - entry->getter = js_mkundef(); - entry->setter = js_mkundef(); - - HASH_ADD(hh, desc_registry, key, sizeof(uint64_t), entry); - } + entry = (descriptor_entry_t *)ant_calloc(sizeof(descriptor_entry_t) + klen + 1); + if (!entry) return NULL; + + entry->key = make_desc_key(obj, key, klen); + entry->obj_off = (uintptr_t)vdata(obj); + entry->prop_name = (char *)(entry + 1); + memcpy(entry->prop_name, key, klen); + entry->prop_name[klen] = '\0'; + entry->prop_len = klen; + entry->writable = true; + entry->enumerable = true; + entry->configurable = true; + entry->has_getter = false; + entry->has_setter = false; + entry->getter = js_mkundef(); + entry->setter = js_mkundef(); + + HASH_ADD(hh, desc_registry, key, sizeof(uint64_t), entry); return entry; } -static void ensure_string_prop_node(ant_t *js, ant_value_t obj, const char *key, size_t klen) { - if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); - if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) return; - if (lkp(js, obj, key, klen) != 0) return; +static descriptor_entry_t *get_or_create_sym_desc(ant_value_t obj, ant_offset_t sym_off) { + if (!desc_registry_allowed(obj)) return NULL; + descriptor_entry_t *entry = registry_lookup_sym_desc(obj, sym_off); + if (entry) return entry; + + entry = (descriptor_entry_t *)ant_calloc(sizeof(descriptor_entry_t)); + if (!entry) return NULL; - ant_value_t key_val = js_mkstr(js, key, klen); - if (is_err(key_val)) return; - (void)mkprop(js, obj, key_val, js_mkundef(), 0); + entry->key = make_sym_desc_key(obj, sym_off); + entry->obj_off = (uintptr_t)vdata(obj); + entry->sym_off = sym_off; + entry->writable = true; + entry->enumerable = true; + entry->configurable = true; + entry->has_getter = false; + entry->has_setter = false; + entry->getter = js_mkundef(); + entry->setter = js_mkundef(); + + HASH_ADD(hh, desc_registry, key, sizeof(uint64_t), entry); + return entry; } -static descriptor_entry_t *get_or_create_sym_desc(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { - if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); - if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) return NULL; - ant_offset_t obj_off = (ant_offset_t)vdata(obj); - uint64_t k = make_sym_desc_key(obj_off, sym_off); +static bool ensure_string_shape_slot(ant_t *js, ant_value_t obj, const char *key, size_t klen, uint32_t *out_slot) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return false; - descriptor_entry_t *entry = NULL; - HASH_FIND(hh, desc_registry, &k, sizeof(uint64_t), entry); - if (!entry) { - entry = (descriptor_entry_t *)ant_calloc(sizeof(descriptor_entry_t)); - if (!entry) return NULL; - entry->key = k; - entry->obj_off = obj_off; - entry->sym_off = sym_off; - entry->prop_name = NULL; - entry->prop_len = 0; - entry->writable = true; - entry->enumerable = true; - entry->configurable = true; - entry->getter = js_mkundef(); - entry->setter = js_mkundef(); - HASH_ADD(hh, desc_registry, key, sizeof(uint64_t), entry); + const char *interned = intern_string(key, klen); + if (!interned) return false; + + int32_t slot = ant_shape_lookup_interned(ptr->shape, interned); + if (slot < 0) { + ant_value_t key_val = js_mkstr(js, key, klen); + if (is_err(key_val)) return false; + if (is_err(mkprop(js, obj, key_val, js_mkundef(), 0))) return false; + slot = ant_shape_lookup_interned(ptr->shape, interned); + if (slot < 0) return false; } - return entry; + + if (out_slot) *out_slot = (uint32_t)slot; + return true; } -static void ensure_symbol_prop_node(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { - if (vtype(obj) == T_FUNC) obj = js_func_obj(obj); - if (vtype(obj) != T_OBJ && vtype(obj) != T_ARR) return; - if (lkp_sym(js, obj, sym_off) != 0) return; +static bool ensure_symbol_shape_slot(ant_t *js, ant_value_t obj, ant_offset_t sym_off, uint32_t *out_slot) { + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return false; - ant_value_t sym = mkval(T_SYMBOL, sym_off); - (void)mkprop(js, obj, sym, js_mkundef(), 0); + int32_t slot = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (slot < 0) { + ant_value_t sym = mkval(T_SYMBOL, sym_off); + if (is_err(mkprop(js, obj, sym, js_mkundef(), 0))) return false; + slot = ant_shape_lookup_symbol(ptr->shape, sym_off); + if (slot < 0) return false; + } + + if (out_slot) *out_slot = (uint32_t)slot; + return true; } -void js_set_descriptor(ant_t *js, ant_value_t obj, const char *key, size_t klen, int flags) { - descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); +static ant_shape_prop_t *ensure_string_shape_prop(ant_t *js, ant_value_t obj, const char *key, size_t klen) { + uint32_t slot = 0; + if (!ensure_string_shape_slot(js, obj, key, klen, &slot)) return NULL; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return NULL; + if (!js_obj_ensure_unique_shape(ptr)) return NULL; + return ant_shape_prop_mut_at(ptr->shape, slot); +} + +static ant_shape_prop_t *ensure_symbol_shape_prop(ant_t *js, ant_value_t obj, ant_offset_t sym_off) { + uint32_t slot = 0; + if (!ensure_symbol_shape_slot(js, obj, sym_off, &slot)) return NULL; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || !ptr->shape) return NULL; + if (!js_obj_ensure_unique_shape(ptr)) return NULL; + return ant_shape_prop_mut_at(ptr->shape, slot); +} + +static void apply_shape_desc_update( + ant_shape_prop_t *prop, + int flags, + bool include_writable, + bool set_getter_flag, + bool has_getter, + ant_value_t getter, + bool set_setter_flag, + bool has_setter, + ant_value_t setter +) { + if (!prop) return; + prop->attrs = attrs_from_flags(flags, include_writable); + if (set_getter_flag) { + prop->has_getter = has_getter ? 1 : 0; + prop->getter = has_getter ? getter : js_mkundef(); + } + if (set_setter_flag) { + prop->has_setter = has_setter ? 1 : 0; + prop->setter = has_setter ? setter : js_mkundef(); + } +} + +static void apply_registry_desc_update( + descriptor_entry_t *entry, + int flags, + bool include_writable, + bool set_getter_flag, + bool has_getter, + ant_value_t getter, + bool set_setter_flag, + bool has_setter, + ant_value_t setter +) { if (!entry) return; - entry->writable = (flags & JS_DESC_W) != 0; + if (include_writable) entry->writable = (flags & JS_DESC_W) != 0; entry->enumerable = (flags & JS_DESC_E) != 0; entry->configurable = (flags & JS_DESC_C) != 0; + if (set_getter_flag) { + entry->has_getter = has_getter; + entry->getter = has_getter ? getter : js_mkundef(); + } + if (set_setter_flag) { + entry->has_setter = has_setter; + entry->setter = has_setter ? setter : js_mkundef(); + } +} + +void js_set_descriptor(ant_t *js, ant_value_t obj, const char *key, size_t klen, int flags) { + assert(is_canonical_desc_obj(obj) && "js_set_descriptor expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; + + ant_shape_prop_t *prop = ensure_string_shape_prop(js, obj, key, klen); + if (prop) { + apply_shape_desc_update( + prop, flags, true, + false, false, js_mkundef(), + false, false, js_mkundef() + ); + ant_ic_epoch_bump(); + return; + } + + if (!desc_registry_allowed(obj)) return; + descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); + apply_registry_desc_update( + entry, flags, true, + false, false, js_mkundef(), + false, false, js_mkundef() + ); } void js_set_getter_desc(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t getter, int flags) { - ensure_string_prop_node(js, obj, key, klen); + assert(is_canonical_desc_obj(obj) && "js_set_getter_desc expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; + + ant_shape_prop_t *prop = ensure_string_shape_prop(js, obj, key, klen); + if (prop) { + apply_shape_desc_update( + prop, flags, false, + true, true, getter, + false, false, js_mkundef() + ); + ant_ic_epoch_bump(); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), getter); + return; + } + + if (!desc_registry_allowed(obj)) return; descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); - if (!entry) return; - entry->enumerable = (flags & JS_DESC_E) != 0; - entry->configurable = (flags & JS_DESC_C) != 0; - entry->has_getter = true; - entry->getter = getter; + apply_registry_desc_update( + entry, flags, false, + true, true, getter, + false, false, js_mkundef() + ); } void js_set_setter_desc(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t setter, int flags) { - ensure_string_prop_node(js, obj, key, klen); + assert(is_canonical_desc_obj(obj) && "js_set_setter_desc expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; + + ant_shape_prop_t *prop = ensure_string_shape_prop(js, obj, key, klen); + if (prop) { + apply_shape_desc_update( + prop, flags, false, + false, false, js_mkundef(), + true, true, setter + ); + ant_ic_epoch_bump(); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), setter); + return; + } + + if (!desc_registry_allowed(obj)) return; descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); - if (!entry) return; - entry->enumerable = (flags & JS_DESC_E) != 0; - entry->configurable = (flags & JS_DESC_C) != 0; - entry->has_setter = true; - entry->setter = setter; + apply_registry_desc_update( + entry, flags, false, + false, false, js_mkundef(), + true, true, setter + ); } void js_set_accessor_desc(ant_t *js, ant_value_t obj, const char *key, size_t klen, ant_value_t getter, ant_value_t setter, int flags) { - ensure_string_prop_node(js, obj, key, klen); + assert(is_canonical_desc_obj(obj) && "js_set_accessor_desc expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; + + ant_shape_prop_t *prop = ensure_string_shape_prop(js, obj, key, klen); + if (prop) { + apply_shape_desc_update( + prop, flags, false, + true, true, getter, + true, true, setter + ); + ant_ic_epoch_bump(); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), getter); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), setter); + return; + } + + if (!desc_registry_allowed(obj)) return; descriptor_entry_t *entry = get_or_create_desc(js, obj, key, klen); - if (!entry) return; - entry->enumerable = (flags & JS_DESC_E) != 0; - entry->configurable = (flags & JS_DESC_C) != 0; - entry->has_getter = true; - entry->has_setter = true; - entry->getter = getter; - entry->setter = setter; + apply_registry_desc_update( + entry, flags, false, + true, true, getter, + true, true, setter + ); } void js_set_sym_getter_desc(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t getter, int flags) { + assert(is_canonical_desc_obj(obj) && "js_set_sym_getter_desc expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; if (vtype(sym) != T_SYMBOL) return; - ensure_symbol_prop_node(js, obj, (ant_offset_t)vdata(sym)); - descriptor_entry_t *entry = get_or_create_sym_desc(js, obj, (ant_offset_t)vdata(sym)); - if (!entry) return; - entry->enumerable = (flags & JS_DESC_E) != 0; - entry->configurable = (flags & JS_DESC_C) != 0; - entry->has_getter = true; - entry->getter = getter; + ant_offset_t sym_off = (ant_offset_t)vdata(sym); + + ant_shape_prop_t *prop = ensure_symbol_shape_prop(js, obj, sym_off); + if (prop) { + apply_shape_desc_update( + prop, flags, false, + true, true, getter, + false, false, js_mkundef() + ); + ant_ic_epoch_bump(); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), getter); + return; + } + + if (!desc_registry_allowed(obj)) return; + descriptor_entry_t *entry = get_or_create_sym_desc(obj, sym_off); + apply_registry_desc_update( + entry, flags, false, + true, true, getter, + false, false, js_mkundef() + ); } void js_set_sym_setter_desc(ant_t *js, ant_value_t obj, ant_value_t sym, ant_value_t setter, int flags) { + assert(is_canonical_desc_obj(obj) && "js_set_sym_setter_desc expects js_as_obj(...)"); + if (!is_canonical_desc_obj(obj)) return; if (vtype(sym) != T_SYMBOL) return; - ensure_symbol_prop_node(js, obj, (ant_offset_t)vdata(sym)); - descriptor_entry_t *entry = get_or_create_sym_desc(js, obj, (ant_offset_t)vdata(sym)); - if (!entry) return; - entry->enumerable = (flags & JS_DESC_E) != 0; - entry->configurable = (flags & JS_DESC_C) != 0; - entry->has_setter = true; - entry->setter = setter; + ant_offset_t sym_off = (ant_offset_t)vdata(sym); + + ant_shape_prop_t *prop = ensure_symbol_shape_prop(js, obj, sym_off); + if (prop) { + apply_shape_desc_update( + prop, flags, false, + false, false, js_mkundef(), + true, true, setter + ); + ant_ic_epoch_bump(); + gc_write_barrier(js, js_obj_ptr(js_as_obj(obj)), setter); + return; + } + + if (!desc_registry_allowed(obj)) return; + descriptor_entry_t *entry = get_or_create_sym_desc(obj, sym_off); + apply_registry_desc_update( + entry, flags, false, + false, false, js_mkundef(), + true, true, setter + ); } diff --git a/src/errors.c b/src/errors.c index f3fe30a..7c58308 100644 --- a/src/errors.c +++ b/src/errors.c @@ -1,5 +1,6 @@ #include "errors.h" #include "internal.h" +#include "descriptors.h" #include "silver/engine.h" #include "modules/io.h" #include "highlight.h" @@ -29,7 +30,7 @@ static void print_error_value(ant_t *js, ant_value_t value, ant_value_t fallback if (!stack && vtype(fallback_stack) == T_STR) { ant_offset_t slen; ant_offset_t soff = vstr(js, fallback_stack, &slen); - stack = (const char *)&js->mem[soff]; + stack = (const char *)(uintptr_t)(soff); } if (prefix) fputs(prefix, stderr); @@ -207,7 +208,6 @@ static size_t append_error_value(errbuf_t *eb, ant_t *js, size_t used, ant_value [T_ARR] = &&l_type_default, [T_PROMISE] = &&l_type_default, [T_GENERATOR] = &&l_type_default, - [T_PROP] = &&l_type_default, [T_BIGINT] = &&l_type_default, [T_NUM] = &&l_type_default, [T_BOOL] = &&l_type_default, @@ -227,7 +227,7 @@ static size_t append_error_value(errbuf_t *eb, ant_t *js, size_t used, ant_value goto l_type_default; l_type_str: - msg = (const char *)&js->mem[vstr(js, value, &msg_len)]; + msg = (const char *)(uintptr_t)(vstr(js, value, &msg_len)); goto l_type_done; l_type_obj: @@ -438,6 +438,27 @@ static bool error_visit_frame_append_errbuf(ant_t *js, const js_vm_frame_view_t return c->remaining > 20; } +ant_value_t js_capture_raw_stack(ant_t *js) { + errbuf_t eb = { malloc(4096), 4096 }; + if (!eb.buf) return js_mkundef(); + eb.buf[0] = '\0'; + + size_t n = 0; + const char *file = (js->errsite.valid && js->errsite.filename) + ? js->errsite.filename + : (js->filename ? js->filename : ""); + + sv_vm_t *vm = js->vm; + if (vm && vm->fp >= 0) { + error_frame_errbuf_ctx_t ctx = { &eb, &n, remaining_capacity(n, eb.size), "", "" }; + error_visit_vm_stack_frames(js, file, error_visit_frame_append_errbuf, &ctx); + } + + ant_value_t stack_str = js_mkstr(js, eb.buf, n); + free(eb.buf); + return stack_str; +} + static bool error_visit_frame_print_file(ant_t *js, const js_vm_frame_view_t *view, void *ctx) { FILE *out = (FILE *)ctx; fprintf( @@ -711,17 +732,18 @@ static ant_value_t js_build_stack_text(ant_t *js, js_stack_text_kind_t kind, ant return stack_str; } -static void js_capture_stack(ant_t *js, ant_value_t err_obj) { +void js_capture_stack(ant_t *js, ant_value_t err_obj) { ant_value_t stack_str = js_build_stack_text(js, JS_STACK_TEXT_FROM_ERROR_OBJECT, err_obj); if (vtype(stack_str) != T_STR) return; js_set(js, err_obj, "stack", stack_str); + js_set_descriptor(js, js_as_obj(err_obj), "stack", 5, JS_DESC_W | JS_DESC_C); js_clear_error_site(js); } js_err_type_t get_error_type(ant_t *js) { if (!js->thrown_exists) return JS_ERR_GENERIC; - ant_value_t err_type = js_get_slot(js, js->thrown_value, SLOT_ERR_TYPE); + ant_value_t err_type = js_get_slot(js->thrown_value, SLOT_ERR_TYPE); if (vtype(err_type) != T_NUM) return JS_ERR_GENERIC; return (js_err_type_t)((int)js_getnum(err_type) & ~JS_ERR_NO_STACK); } @@ -745,7 +767,7 @@ ant_value_t js_create_error(ant_t *js, js_err_type_t err_type, ant_value_t props ant_value_t err_obj = js_mkobj(js); js_set(js, err_obj, "name", js_mkstr(js, err_name, err_name_len)); js_set(js, err_obj, "message", js_mkstr(js, error_msg, msg_len)); - js_set_slot(js, err_obj, SLOT_ERR_TYPE, js_mknum((double)err_type)); + js_set_slot(err_obj, SLOT_ERR_TYPE, js_mknum((double)err_type)); int props_type = vtype(props); if ((JS_TYPE_FLAG(props_type) & T_SPECIAL_OBJECT_MASK) != 0) { @@ -754,7 +776,7 @@ ant_value_t js_create_error(ant_t *js, js_err_type_t err_type, ant_value_t props ant_value_t proto = js_get_ctor_proto(js, err_name, err_name_len); int proto_type = vtype(proto); if ((JS_TYPE_FLAG(proto_type) & T_SPECIAL_OBJECT_MASK) != 0) { - js_set_proto(js, err_obj, proto); + js_set_proto_init(err_obj, proto); } js->thrown_exists = true; @@ -777,7 +799,7 @@ ant_value_t js_throw(ant_t *js, ant_value_t value) { js_clear_error_site(js); return mkval(T_ERR, vdata(value)); } - ant_value_t slot = js_get_slot(js, value, SLOT_ERR_TYPE); + ant_value_t slot = js_get_slot(value, SLOT_ERR_TYPE); if (vtype(slot) == T_NUM && ((int)js_getnum(slot) & JS_ERR_NO_STACK)) { js->thrown_exists = true; js->thrown_value = value; @@ -791,6 +813,7 @@ ant_value_t js_throw(ant_t *js, ant_value_t value) { if (vtype(value) == T_OBJ) { js_set(js, value, "stack", stack_str); + js_set_descriptor(js, js_as_obj(value), "stack", 5, JS_DESC_W | JS_DESC_C); } js->thrown_exists = true; diff --git a/src/esm/commonjs.c b/src/esm/commonjs.c index 1957c52..793b6cb 100644 --- a/src/esm/commonjs.c +++ b/src/esm/commonjs.c @@ -17,20 +17,20 @@ static ant_value_t esm_cjs_require(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr(js, "require() expects a string specifier"); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t data = js_get_slot(js, fn, SLOT_DATA); + ant_value_t data = js_get_slot(fn, SLOT_DATA); const char *base_path = js_module_eval_active_filename(js); if (vtype(data) == T_STR) { ant_offset_t path_len = 0; ant_offset_t path_off = vstr(js, data, &path_len); - base_path = (const char *)&js->mem[path_off]; + base_path = (const char *)(uintptr_t)(path_off); } ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path); if (is_err(ns)) return ns; if (vtype(ns) == T_OBJ) { - ant_value_t default_export = js_get_slot(js, ns, SLOT_DEFAULT); + ant_value_t default_export = js_get_slot(ns, SLOT_DEFAULT); if (vtype(default_export) != T_UNDEF) return default_export; } return ns; @@ -42,13 +42,13 @@ static ant_value_t esm_cjs_require_resolve(ant_t *js, ant_value_t *args, int nar } ant_value_t fn = js_getcurrentfunc(js); - ant_value_t data = js_get_slot(js, fn, SLOT_DATA); + ant_value_t data = js_get_slot(fn, SLOT_DATA); const char *base_path = js_module_eval_active_filename(js); if (vtype(data) == T_STR) { ant_offset_t data_len = 0; ant_offset_t data_off = vstr(js, data, &data_len); - base_path = (const char *)&js->mem[data_off]; + base_path = (const char *)(uintptr_t)(data_off); } ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path); @@ -58,7 +58,7 @@ static ant_value_t esm_cjs_require_resolve(ant_t *js, ant_value_t *args, int nar ant_offset_t len = 0; ant_offset_t off = vstr(js, resolved, &len); - const char *s = (const char *)&js->mem[off]; + const char *s = (const char *)(uintptr_t)(off); static const char *prefix = "file://"; if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) { @@ -74,7 +74,7 @@ static ant_value_t esm_populate_cjs_namespace(ant_t *js, ant_value_t ns, ant_val ant_value_t set_default = setprop_cstr(js, ns, "default", 7, exports_val); if (is_err(set_default)) return set_default; - js_set_slot(js, ns, SLOT_DEFAULT, exports_val); + js_set_slot_wb(js, ns, SLOT_DEFAULT, exports_val); if (!is_object_type(exports_val)) return js_mkundef(); ant_iter_t iter = js_prop_iter_begin(js, exports_val); diff --git a/src/esm/library.c b/src/esm/library.c index 2e563f7..806f3fd 100644 --- a/src/esm/library.c +++ b/src/esm/library.c @@ -1,7 +1,5 @@ #include "esm/library.h" - #include "ant.h" -#include "arena.h" #include #include diff --git a/src/esm/loader.c b/src/esm/loader.c index 9e6e9a0..ca5a34f 100644 --- a/src/esm/loader.c +++ b/src/esm/loader.c @@ -9,6 +9,7 @@ #include "modules/napi.h" #include "errors.h" +#include "gc/modules.h" #include "internal.h" #include "reactor.h" #include "utils.h" @@ -134,7 +135,7 @@ static char *esm_try_resolve_index_with_exts(const char *dir, const char *spec) } static ant_value_t esm_default_export_or_namespace(ant_t *js, ant_value_t ns) { - ant_value_t default_val = js_get_slot(js, ns, SLOT_DEFAULT); + ant_value_t default_val = js_get_slot(ns, SLOT_DEFAULT); return vtype(default_val) != T_UNDEF ? default_val : ns; } @@ -888,15 +889,6 @@ void js_esm_cleanup_module_cache(void) { global_module_cache.count = 0; } -void js_esm_gc_roots(void (*visit)(void *ctx, ant_value_t *val), void *ctx) { - if (!visit) return; - esm_module_t *mod = NULL, *tmp = NULL; - HASH_ITER(hh, global_module_cache.modules, mod, tmp) { - visit(ctx, &mod->namespace_obj); - visit(ctx, &mod->default_export); - } -} - static ant_value_t esm_read_file(ant_t *js, const char *path, const char *kind, esm_file_data_t *out) { FILE *fp = fopen(path, "rb"); if (!fp) return js_mkerr(js, "Cannot open %s: %s", kind, path); @@ -1125,6 +1117,7 @@ ant_value_t js_esm_import_sync_cstr_from( if (file_url_path) { free(spec_copy); spec_copy = file_url_path; + spec_len = strlen(spec_copy); } bool loaded = false; @@ -1158,7 +1151,7 @@ ant_value_t js_esm_import_sync_from(ant_t *js, ant_value_t specifier, const char ant_offset_t spec_len = 0; ant_offset_t spec_off = vstr(js, specifier, &spec_len); - const char *spec_str = (const char *)&js->mem[spec_off]; + const char *spec_str = (const char *)(uintptr_t)(spec_off); return js_esm_import_sync_cstr_from(js, spec_str, (size_t)spec_len, base_path); } @@ -1178,6 +1171,14 @@ ant_value_t js_esm_make_file_url(ant_t *js, const char *path) { return val; } +void gc_mark_esm(ant_t *js, gc_mark_fn mark) { + esm_module_t *mod = NULL, *tmp = NULL; + HASH_ITER(hh, global_module_cache.modules, mod, tmp) { + mark(js, mod->namespace_obj); + mark(js, mod->default_export); + } +} + ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const char *base_path) { if (vtype(specifier) != T_STR) { return js_mkerr(js, "import.meta.resolve() requires a string specifier"); @@ -1185,7 +1186,7 @@ ant_value_t js_esm_resolve_specifier(ant_t *js, ant_value_t specifier, const cha ant_offset_t spec_len = 0; ant_offset_t spec_off = vstr(js, specifier, &spec_len); - const char *spec_str = (const char *)&js->mem[spec_off]; + const char *spec_str = (const char *)(uintptr_t)(spec_off); char *spec_copy = strndup(spec_str, (size_t)spec_len); if (!spec_copy) return js_mkerr(js, "oom"); diff --git a/src/gc.c b/src/gc.c deleted file mode 100644 index 6c0a66d..0000000 --- a/src/gc.c +++ /dev/null @@ -1,1128 +0,0 @@ -#include "gc.h" -#include "arena.h" -#include "internal.h" -#include "common.h" -#include "sugar.h" -#include "silver/engine.h" - -#include -#include -#include -#include -#include - -#if defined(__has_feature) -#if __has_feature(address_sanitizer) -#include -#define ANT_HAS_ASAN 1 -#endif -#endif - -#ifndef ANT_HAS_ASAN -#define ANT_HAS_ASAN 0 -#endif - -#ifdef _WIN32 -#define WIN32_LEAN_AND_MEAN -#include -static inline void *gc_mmap(size_t size) { - return VirtualAlloc(NULL, size, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE); -} -static inline void gc_munmap(void *ptr, size_t size) { - VirtualFree(ptr, 0, MEM_RELEASE); -} -#else -#include -static inline void *gc_mmap(size_t size) { - void *p = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0); - return (p == MAP_FAILED) ? NULL : p; -} -static inline void gc_munmap(void *ptr, size_t size) { - munmap(ptr, size); -} -#endif - -#define MCO_API extern -#include "minicoro.h" - -uint32_t gc_epoch_counter; -static uint8_t *gc_scratch_buf = NULL; - -static size_t gc_scratch_size = 0; -static time_t gc_last_run_time = 0; - -static bool gc_throttled = false; -void js_gc_throttle(bool enabled) { gc_throttled = enabled; } - -typedef struct { - ant_offset_t *old_offs; - ant_offset_t *new_offs; - size_t count; - size_t capacity; - size_t mask; -} gc_forward_table_t; - -typedef struct { - ant_offset_t *items; - size_t count; - size_t capacity; -} gc_work_queue_t; - -typedef struct { - ant_t *js; - uint8_t *new_mem; - uint8_t *mark_bits; - gc_forward_table_t fwd; - gc_work_queue_t work; - ant_offset_t new_brk; - ant_offset_t new_size; - bool failed; -} gc_ctx_t; - -static ant_value_t gc_update_val(gc_ctx_t *ctx, ant_value_t val); -static ant_value_t gc_apply_val(gc_ctx_t *ctx, ant_value_t val); - -/* inlined helpers from ant.c */ -static inline bool gc_func_const_count_sane(int n) { - return n >= 0 && n <= (1 << 20); -} - -static inline bool gc_upvalue_is_closed(const sv_upvalue_t *uv) { - return uv && uv->location == &uv->closed; -} - -static inline bool gc_is_tagged(ant_value_t v) { - return v > NANBOX_PREFIX; -} - -static inline uint8_t gc_vtype(ant_value_t v) { - return gc_is_tagged(v) ? ((v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK) : 255; -} - -static inline size_t gc_vdata(ant_value_t v) { - return (size_t)(v & NANBOX_DATA_MASK); -} - -static inline ant_value_t gc_mkval(uint8_t type, uint64_t data) { - return NANBOX_PREFIX - | ((ant_value_t)(type & NANBOX_TYPE_MASK) << NANBOX_TYPE_SHIFT) - | (data & NANBOX_DATA_MASK); -} - -static inline void mark_set(gc_ctx_t *ctx, ant_offset_t off) { - ant_offset_t idx = off >> 3; - ctx->mark_bits[idx >> 3] |= (1 << (idx & 7)); -} - -static inline size_t next_pow2(size_t n) { - n--; - n |= n >> 1; n |= n >> 2; n |= n >> 4; - n |= n >> 8; n |= n >> 16; -#if SIZE_MAX > 0xFFFFFFFF - n |= n >> 32; -#endif - return n + 1; -} - -static inline bool gc_stack_word_readable(uintptr_t addr) { -#if ANT_HAS_ASAN - return __asan_region_is_poisoned((void *)addr, sizeof(uint64_t)) == NULL; -#else - return true; -#endif -} - -static inline bool gc_off_has_bytes(ant_offset_t off, ant_offset_t need, ant_offset_t brk) { - return off <= brk && need <= brk - off; -} - -static void gc_update_func_constants(gc_ctx_t *ctx, sv_func_t *func, int depth) { - if (!func || depth > 1024) return; - if (func->gc_epoch == gc_epoch_counter) return; - - func->gc_epoch = gc_epoch_counter; - if (!func->constants || !gc_func_const_count_sane(func->const_count)) return; - - for (int i = 0; i < func->const_count; i++) { - ant_value_t c = func->constants[i]; - - if (vtype(c) == T_CFUNC) { - sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(c); - gc_update_func_constants(ctx, child, depth + 1); - continue; - } - - func->constants[i] = gc_update_val(ctx, c); - } -} - -static inline void gc_update_closure(gc_ctx_t *ctx, sv_closure_t *closure) { - if (!closure) return; - if (closure->gc_epoch == gc_epoch_counter) return; - - closure->gc_epoch = gc_epoch_counter; - closure->func_obj = gc_update_val(ctx, closure->func_obj); - closure->bound_this = gc_update_val(ctx, closure->bound_this); - closure->bound_args = gc_update_val(ctx, closure->bound_args); - closure->super_val = gc_update_val(ctx, closure->super_val); - - if (closure->bound_argv && closure->bound_argc > 0) { - for (int i = 0; i < closure->bound_argc; i++) - closure->bound_argv[i] = gc_update_val(ctx, closure->bound_argv[i]); - } - - if (!closure->func) return; - gc_update_func_constants(ctx, closure->func, 0); - - if (!closure->upvalues) return; - int n = closure->func->upvalue_count; - if (n <= 0 || n > (int)UINT16_MAX) return; - - for (int i = 0; i < n; i++) { - sv_upvalue_t *uv = closure->upvalues[i]; - if (!gc_upvalue_is_closed(uv)) continue; - if (uv->gc_epoch == gc_epoch_counter) continue; - uv->gc_epoch = gc_epoch_counter; - uv->closed = gc_update_val(ctx, uv->closed); - } -} - -static bool fwd_init(gc_forward_table_t *fwd, size_t estimated) { - size_t cap = next_pow2(estimated < 64 ? 64 : estimated); - size_t size = cap * sizeof(ant_offset_t); - - fwd->old_offs = (ant_offset_t *)gc_mmap(size); - fwd->new_offs = (ant_offset_t *)gc_mmap(size); - - if (!fwd->old_offs || !fwd->new_offs) { - if (fwd->old_offs) gc_munmap(fwd->old_offs, size); - if (fwd->new_offs) gc_munmap(fwd->new_offs, size); - return false; - } - - for (size_t i = 0; i < cap; i++) fwd->old_offs[i] = FWD_EMPTY; - - fwd->count = 0; - fwd->capacity = cap; - fwd->mask = cap - 1; - - return true; -} - -static bool fwd_grow(gc_forward_table_t *fwd) { - size_t new_cap = fwd->capacity * 2; - size_t new_mask = new_cap - 1; - size_t new_size = new_cap * sizeof(ant_offset_t); - size_t old_size = fwd->capacity * sizeof(ant_offset_t); - - ant_offset_t *new_old = (ant_offset_t *)gc_mmap(new_size); - ant_offset_t *new_new = (ant_offset_t *)gc_mmap(new_size); - - if (!new_old || !new_new) { - if (new_old) gc_munmap(new_old, new_size); - if (new_new) gc_munmap(new_new, new_size); - return false; - } - - for (size_t i = 0; i < new_cap; i++) new_old[i] = FWD_EMPTY; - - for (size_t i = 0; i < fwd->capacity; i++) { - ant_offset_t key = fwd->old_offs[i]; - if (key == FWD_EMPTY || key == FWD_TOMBSTONE) continue; - size_t h = (key >> 3) & new_mask; - while (new_old[h] != FWD_EMPTY) h = (h + 1) & new_mask; - new_old[h] = key; - new_new[h] = fwd->new_offs[i]; - } - - gc_munmap(fwd->old_offs, old_size); - gc_munmap(fwd->new_offs, old_size); - - fwd->old_offs = new_old; - fwd->new_offs = new_new; - fwd->capacity = new_cap; - fwd->mask = new_mask; - - return true; -} - -static inline bool fwd_add(gc_forward_table_t *fwd, ant_offset_t old_off, ant_offset_t new_off) { - if (fwd->count * 100 >= fwd->capacity * GC_FWD_LOAD_FACTOR) { - if (!fwd_grow(fwd)) return false; - } - - size_t h = (old_off >> 3) & fwd->mask; - while (fwd->old_offs[h] != FWD_EMPTY && fwd->old_offs[h] != FWD_TOMBSTONE) { - if (fwd->old_offs[h] == old_off) { - fwd->new_offs[h] = new_off; - return true; - } - h = (h + 1) & fwd->mask; - } - - fwd->old_offs[h] = old_off; - fwd->new_offs[h] = new_off; - fwd->count++; - - return true; -} - -static inline ant_offset_t fwd_lookup(gc_forward_table_t *fwd, ant_offset_t old_off) { - size_t h = (old_off >> 3) & fwd->mask; - for (size_t i = 0; i < fwd->capacity; i++) { - ant_offset_t key = fwd->old_offs[h]; - if (key == FWD_EMPTY) return (ant_offset_t)~0; - if (key == old_off) return fwd->new_offs[h]; - h = (h + 1) & fwd->mask; - } - return (ant_offset_t)~0; -} - -static void fwd_free(gc_forward_table_t *fwd) { - size_t size = fwd->capacity * sizeof(ant_offset_t); - if (fwd->old_offs) gc_munmap(fwd->old_offs, size); - if (fwd->new_offs) gc_munmap(fwd->new_offs, size); - - fwd->old_offs = NULL; - fwd->new_offs = NULL; - fwd->count = 0; - fwd->capacity = 0; -} - -static bool work_init(gc_work_queue_t *work, size_t initial) { - size_t size = initial * sizeof(ant_offset_t); - work->items = (ant_offset_t *)gc_mmap(size); - if (!work->items) return false; - work->count = 0; - work->capacity = initial; - return true; -} - -static inline bool work_push(gc_work_queue_t *work, ant_offset_t off) { - if (work->count >= work->capacity) { - size_t new_cap = work->capacity * 2; - size_t new_size = new_cap * sizeof(ant_offset_t); - size_t old_size = work->capacity * sizeof(ant_offset_t); - - ant_offset_t *new_items = (ant_offset_t *)gc_mmap(new_size); - if (!new_items) return false; - - memcpy(new_items, work->items, work->count * sizeof(ant_offset_t)); - gc_munmap(work->items, old_size); - - work->items = new_items; - work->capacity = new_cap; - } - - work->items[work->count++] = off; - return true; -} - -static inline ant_offset_t work_pop(gc_work_queue_t *work) { - if (work->count == 0) return (ant_offset_t)~0; - return work->items[--work->count]; -} - -static void work_free(gc_work_queue_t *work) { - size_t size = work->capacity * sizeof(ant_offset_t); - if (work->items) gc_munmap(work->items, size); - - work->items = NULL; - work->count = 0; - work->capacity = 0; -} - -static inline ant_offset_t gc_loadoff(uint8_t *mem, ant_offset_t off) { - ant_offset_t val; - memcpy(&val, &mem[off], sizeof(val)); - return val; -} - -static inline ant_value_t gc_loadval(uint8_t *mem, ant_offset_t off) { - ant_value_t val; - memcpy(&val, &mem[off], sizeof(val)); - return val; -} - -static inline void gc_saveoff(uint8_t *mem, ant_offset_t off, ant_offset_t val) { - memcpy(&mem[off], &val, sizeof(val)); -} - -static inline void gc_saveval(uint8_t *mem, ant_offset_t off, ant_value_t val) { - memcpy(&mem[off], &val, sizeof(val)); -} - -static ant_offset_t gc_alloc(gc_ctx_t *ctx, size_t size) { - size = (size + 7) / 8 * 8; - if (ctx->new_brk + size > ctx->new_size) { - ctx->failed = true; - return (ant_offset_t)~0; - } - ant_offset_t off = ctx->new_brk; - ctx->new_brk += (ant_offset_t)size; - return off; -} - - -static ant_offset_t gc_copy_string(gc_ctx_t *ctx, ant_offset_t old_off) { - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - if ((header & 3) != T_STR) return old_off; - - bool is_rope_str = (header & ROPE_FLAG) != 0; - ant_offset_t size; - - if (is_rope_str) { - size = sizeof(rope_node_t); - } else size = esize(header); - - if (size == (ant_offset_t)~0) return old_off; - - new_off = gc_alloc(ctx, size); - if (new_off == (ant_offset_t)~0) return old_off; - - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); - if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; - mark_set(ctx, old_off); - - if (is_rope_str) { - ant_value_t left, right, cached; - memcpy(&left, &ctx->js->mem[old_off + offsetof(rope_node_t, left)], sizeof(ant_value_t)); - memcpy(&right, &ctx->js->mem[old_off + offsetof(rope_node_t, right)], sizeof(ant_value_t)); - memcpy(&cached, &ctx->js->mem[old_off + offsetof(rope_node_t, cached)], sizeof(ant_value_t)); - - ant_value_t new_left = gc_update_val(ctx, left); - ant_value_t new_right = gc_update_val(ctx, right); - ant_value_t new_cached = gc_update_val(ctx, cached); - - memcpy(&ctx->new_mem[new_off + offsetof(rope_node_t, left)], &new_left, sizeof(ant_value_t)); - memcpy(&ctx->new_mem[new_off + offsetof(rope_node_t, right)], &new_right, sizeof(ant_value_t)); - memcpy(&ctx->new_mem[new_off + offsetof(rope_node_t, cached)], &new_cached, sizeof(ant_value_t)); - } - - return new_off; -} - -typedef struct { - ant_offset_t total_aligned; - ant_offset_t limbs_off; - uint32_t limb_count; - uint8_t sign; -} gc_bigint_meta_t; - -static bool gc_read_bigint_meta( - gc_ctx_t *ctx, - ant_offset_t old_off, - ant_offset_t old_brk, - gc_bigint_meta_t *meta -) { - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - if ((header & GC_BIGINT_HEADER_LOW_MASK) != 0) return false; - - ant_offset_t payload = header >> GC_BIGINT_HEADER_SHIFT; - const ant_offset_t fixed_payload = (ant_offset_t)(sizeof(uint32_t) + sizeof(uint32_t)); - if (payload < fixed_payload + (ant_offset_t)sizeof(uint32_t)) return false; - - ant_offset_t total = payload + (ant_offset_t)sizeof(ant_offset_t); - total = (total + 7) & ~(ant_offset_t)7; - if (!gc_off_has_bytes(old_off, total, old_brk)) return false; - - ant_offset_t sign_off = old_off + (ant_offset_t)sizeof(ant_offset_t); - uint8_t sign = ctx->js->mem[sign_off]; - if (sign > 1) return false; - - ant_offset_t count_off = old_off + (ant_offset_t)sizeof(ant_offset_t) + (ant_offset_t)sizeof(uint32_t); - uint32_t limb_count = 0; - memcpy(&limb_count, &ctx->js->mem[count_off], sizeof(limb_count)); - if (limb_count == 0) return false; - - ant_offset_t expected_payload = fixed_payload + (ant_offset_t)limb_count * (ant_offset_t)sizeof(uint32_t); - if (expected_payload != payload) return false; - - ant_offset_t limbs_off = count_off + (ant_offset_t)sizeof(uint32_t); - uint32_t top_limb = 0; - memcpy( - &top_limb, - &ctx->js->mem[limbs_off + (ant_offset_t)(limb_count - 1) * (ant_offset_t)sizeof(uint32_t)], - sizeof(top_limb) - ); - if (top_limb == 0 && limb_count > 1) return false; - - if (limb_count == 1) { - uint32_t limb0 = 0; - memcpy(&limb0, &ctx->js->mem[limbs_off], sizeof(limb0)); - if (limb0 == 0 && sign != 0) return false; - } - - if (meta) { - meta->total_aligned = total; - meta->limbs_off = limbs_off; - meta->limb_count = limb_count; - meta->sign = sign; - } - - return true; -} - -static ant_offset_t gc_copy_bigint(gc_ctx_t *ctx, ant_offset_t old_off) { - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - gc_bigint_meta_t meta; - if (!gc_read_bigint_meta(ctx, old_off, ctx->js->brk, &meta)) return old_off; - - new_off = gc_alloc(ctx, (size_t)meta.total_aligned); - if (new_off == (ant_offset_t)~0) return old_off; - - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], (size_t)meta.total_aligned); - if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; - mark_set(ctx, old_off); - - return new_off; -} - -static ant_offset_t gc_copy_symbol(gc_ctx_t *ctx, ant_offset_t old_off) { - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - size_t total = (header >> GC_SYM_HEADER_SHIFT) + sizeof(ant_offset_t); - total = (total + 7) / 8 * 8; - - new_off = gc_alloc(ctx, total); - if (new_off == (ant_offset_t)~0) return old_off; - - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], total); - if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; - mark_set(ctx, old_off); - - return new_off; -} - -static ant_offset_t gc_reserve_object(gc_ctx_t *ctx, ant_offset_t old_off) { - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - if ((header & 3) != T_OBJ) return old_off; - - ant_offset_t size = esize(header); - if (size == (ant_offset_t)~0) return old_off; - - new_off = gc_alloc(ctx, size); - if (new_off == (ant_offset_t)~0) return old_off; - - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); - if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; - mark_set(ctx, old_off); - if (!work_push(&ctx->work, old_off)) ctx->failed = true; - - return new_off; -} - -static ant_offset_t gc_reserve_prop(gc_ctx_t *ctx, ant_offset_t old_off) { - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - if ((header & 3) != T_PROP) return old_off; - - ant_offset_t size = esize(header); - if (size == (ant_offset_t)~0) return old_off; - - new_off = gc_alloc(ctx, size); - if (new_off == (ant_offset_t)~0) return old_off; - - memcpy(&ctx->new_mem[new_off], &ctx->js->mem[old_off], size); - if (!fwd_add(&ctx->fwd, old_off, new_off)) ctx->failed = true; - mark_set(ctx, old_off); - if (!work_push(&ctx->work, old_off)) ctx->failed = true; - - return new_off; -} - -static void gc_process_prop(gc_ctx_t *ctx, ant_offset_t old_off) { - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off == (ant_offset_t)~0) return; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - ant_offset_t next_prop = header & ~(3ULL | FLAGMASK); - - if (next_prop != 0 && next_prop < ctx->js->brk) { - ant_offset_t new_next = gc_reserve_prop(ctx, next_prop); - ant_offset_t new_header = (new_next & ~3ULL) | (header & (3ULL | FLAGMASK)); - gc_saveoff(ctx->new_mem, new_off, new_header); - } - - bool is_slot = (header & SLOTMASK) != 0; - - if (!is_slot) { - ant_offset_t key_off = gc_loadoff(ctx->js->mem, old_off + sizeof(ant_offset_t)); - if (key_off < ctx->js->brk) { - ant_offset_t key_hdr = gc_loadoff(ctx->js->mem, key_off); ant_offset_t new_key; - if ((key_hdr & 3) == T_STR) new_key = gc_copy_string(ctx, key_off); - else new_key = gc_copy_symbol(ctx, key_off); - gc_saveoff(ctx->new_mem, new_off + sizeof(ant_offset_t), new_key); - } - } - - ant_value_t val = gc_loadval(ctx->js->mem, old_off + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - if (!is_slot) goto update_val; - - ant_offset_t slot_id = gc_loadoff(ctx->js->mem, old_off + sizeof(ant_offset_t)); - if (slot_id != (ant_offset_t)SLOT_DENSE_BUF) goto update_val; - - ant_offset_t old_doff = (ant_offset_t)tod(val); - if (old_doff == 0 || old_doff >= ctx->js->brk) goto update_val; - - ant_offset_t cap = gc_loadoff(ctx->js->mem, old_doff); - ant_offset_t len = gc_loadoff(ctx->js->mem, old_doff + sizeof(ant_offset_t)); - ant_offset_t buf_size = (ant_offset_t)(sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * cap); - ant_offset_t new_doff = gc_alloc(ctx, buf_size); - if (new_doff == (ant_offset_t)~0) return; - - memcpy(&ctx->new_mem[new_doff], &ctx->js->mem[old_doff], buf_size); - for (ant_offset_t i = 0; i < len; i++) { - ant_offset_t voff = new_doff + (ant_offset_t)(sizeof(ant_offset_t) * 2 + sizeof(ant_value_t) * i); - ant_value_t v = gc_loadval(ctx->new_mem, voff); - if (v != T_EMPTY) { - ant_value_t nv = gc_update_val(ctx, v); - gc_saveval(ctx->new_mem, voff, nv); - } - } - - gc_saveval( - ctx->new_mem, - new_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), - tov((double)new_doff) - ); return; - - update_val: { - ant_value_t new_val = gc_update_val(ctx, val); - gc_saveval(ctx->new_mem, new_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), new_val); - } -} - -static void gc_process_object(gc_ctx_t *ctx, ant_offset_t old_off) { - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off == (ant_offset_t)~0) return; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - - ant_offset_t first_prop = header & ~(3ULL | FLAGMASK); - if (first_prop != 0 && first_prop < ctx->js->brk) { - ant_offset_t new_first = gc_reserve_prop(ctx, first_prop); - ant_offset_t new_header = (new_first & ~3ULL) | (header & (3ULL | FLAGMASK)); - gc_saveoff(ctx->new_mem, new_off, new_header); - } - - ant_offset_t parent_off = gc_loadoff(ctx->js->mem, old_off + sizeof(ant_offset_t)); - if (parent_off < ctx->js->brk) { - ant_offset_t new_parent = gc_reserve_object(ctx, parent_off); - gc_saveoff(ctx->new_mem, new_off + sizeof(ant_offset_t), new_parent); - } - - ant_offset_t tail_off = gc_loadoff(ctx->js->mem, old_off + sizeof(ant_offset_t) + sizeof(ant_offset_t)); - if (tail_off != 0 && tail_off < ctx->js->brk) { - ant_offset_t new_tail = fwd_lookup(&ctx->fwd, tail_off); - if (new_tail == (ant_offset_t)~0) new_tail = gc_reserve_prop(ctx, tail_off); - gc_saveoff(ctx->new_mem, new_off + sizeof(ant_offset_t) + sizeof(ant_offset_t), new_tail); - } -} - -static void gc_drain_work_queue(gc_ctx_t *ctx) { - ant_offset_t off; - while ((off = work_pop(&ctx->work)) != (ant_offset_t)~0) { - ant_offset_t header = gc_loadoff(ctx->js->mem, off); - switch (header & 3) { - case T_OBJ: gc_process_object(ctx, off); break; - case T_PROP: gc_process_prop(ctx, off); break; - default: break; - } - } -} - -static ant_value_t gc_update_val(gc_ctx_t *ctx, ant_value_t val) { - if (!gc_is_tagged(val)) return val; - - uint8_t type = gc_vtype(val); - ant_offset_t old_off = (ant_offset_t)gc_vdata(val); - - switch (type) { - case T_OBJ: - case T_ARR: - case T_PROMISE: - case T_GENERATOR: { - if (old_off >= ctx->js->brk) return val; - ant_offset_t new_off = gc_reserve_object(ctx, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - case T_STR: { - if (old_off >= ctx->js->brk) return val; - ant_offset_t new_off = gc_copy_string(ctx, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - case T_PROP: { - if (old_off >= ctx->js->brk) return val; - ant_offset_t new_off = gc_reserve_prop(ctx, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - case T_BIGINT: { - if (old_off >= ctx->js->brk) return val; - ant_offset_t new_off = gc_copy_bigint(ctx, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - case T_SYMBOL: { - if (old_off >= ctx->js->brk) return val; - ant_offset_t new_off = gc_copy_symbol(ctx, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - case T_FUNC: - case T_CLOSURE: { - gc_update_closure(ctx, (sv_closure_t *)(uintptr_t)gc_vdata(val)); - return val; - } - default: break; - } - - return val; -} - -static ant_offset_t gc_fwd_off_callback(void *ctx_ptr, ant_offset_t old_off) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - if (old_off == 0) return 0; - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return new_off; - - static const void *dispatch[] = { &&l_obj, &&l_prop, &&l_str, &&l_default }; - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - goto *dispatch[header & 3]; - - l_obj: new_off = gc_reserve_object(ctx, old_off); goto l_done; - l_prop: new_off = gc_reserve_prop(ctx, old_off); goto l_done; - l_str: new_off = gc_copy_string(ctx, old_off); goto l_done; - l_default: return old_off; - - l_done: return (new_off != (ant_offset_t)~0) ? new_off : old_off; -} - -static ant_value_t gc_fwd_val_callback(void *ctx_ptr, ant_value_t val) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - return gc_update_val(ctx, val); -} - -static ant_offset_t gc_apply_off_callback(void *ctx_ptr, ant_offset_t old_off) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - if (old_off == 0) return 0; - if (old_off >= ctx->js->brk) return old_off; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - return (new_off != (ant_offset_t)~0) ? new_off : old_off; -} - -static ant_value_t gc_apply_val(gc_ctx_t *ctx, ant_value_t val) { - if (!gc_is_tagged(val)) return val; - - uint8_t type = gc_vtype(val); - if (type == T_FUNC || type == T_CLOSURE) return val; - - ant_offset_t old_off = (ant_offset_t)gc_vdata(val); - if (old_off >= ctx->js->brk) return val; - - switch (type) { - case T_OBJ: - case T_ARR: - case T_PROMISE: - case T_GENERATOR: - case T_STR: - case T_PROP: - case T_BIGINT: - case T_SYMBOL: { - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off != (ant_offset_t)~0) return gc_mkval(type, new_off); - break; - } - default: break; - } - - return val; -} - -static ant_value_t gc_apply_val_callback(void *ctx_ptr, ant_value_t val) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - return gc_apply_val(ctx, val); -} - -static ant_offset_t gc_weak_off_callback(void *ctx_ptr, ant_offset_t old_off) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - if (old_off >= ctx->js->brk) return old_off; - return fwd_lookup(&ctx->fwd, old_off); -} - -static inline bool gc_get_stack_bounds(uintptr_t base, uintptr_t sp, uintptr_t *lo, uintptr_t *hi) { - if (base == 0 || sp == 0) return false; - - uintptr_t minp = (sp < base) ? sp : base; - uintptr_t maxp = (sp < base) ? base : sp; - - uintptr_t aligned_lo = (minp + (uintptr_t)sizeof(uint64_t) - 1u) & ~((uintptr_t)sizeof(uint64_t) - 1u); - uintptr_t aligned_hi = maxp & ~((uintptr_t)sizeof(uint64_t) - 1u); - - if (aligned_lo >= aligned_hi) return false; - *lo = aligned_lo; - *hi = aligned_hi; - return true; -} - -static inline bool gc_stack_word_valid(gc_ctx_t *ctx, uint8_t type, ant_offset_t old_off, ant_offset_t old_brk) { - if (old_off == 0 || old_off >= old_brk) return false; - if (!gc_off_has_bytes(old_off, (ant_offset_t)sizeof(ant_offset_t), old_brk)) return false; - - ant_offset_t header = gc_loadoff(ctx->js->mem, old_off); - - switch (type) { - case T_OBJ: - case T_ARR: - case T_PROMISE: - case T_GENERATOR: { - if ((header & 3) != T_OBJ) return false; - ant_offset_t size = esize(header); - return size != (ant_offset_t)~0 && gc_off_has_bytes(old_off, size, old_brk); - } - case T_PROP: { - if ((header & 3) != T_PROP) return false; - ant_offset_t size = esize(header); - return size != (ant_offset_t)~0 && gc_off_has_bytes(old_off, size, old_brk); - } - case T_STR: { - if ((header & 3) != T_STR) return false; - if ((header & ROPE_FLAG) != 0) - return gc_off_has_bytes(old_off, (ant_offset_t)sizeof(rope_node_t), old_brk); - ant_offset_t size = esize(header); - return size != (ant_offset_t)~0 && gc_off_has_bytes(old_off, size, old_brk); - } - case T_BIGINT: { - return gc_read_bigint_meta(ctx, old_off, old_brk, NULL); - } - case T_SYMBOL: { - if ((header & GC_SYM_HEADER_LOW_MASK) != 0) return false; - ant_offset_t payload = header >> GC_SYM_HEADER_SHIFT; - if (payload < GC_SYM_HEAP_FIXED) return false; - - ant_offset_t total = payload + (ant_offset_t)sizeof(ant_offset_t); - total = (total + 7) & ~(ant_offset_t)7; - return gc_off_has_bytes(old_off, total, old_brk); - } - default: return false; - } -} - -__attribute__((noinline)) -static void gc_scan_stack_reserve(gc_ctx_t *ctx) { - jmp_buf jb; - if (setjmp(jb) != 0) return; - - volatile uint8_t sp_marker = 0; uintptr_t lo, hi; - if (!gc_get_stack_bounds((uintptr_t)ctx->js->cstk.base, (uintptr_t)&sp_marker, &lo, &hi)) return; - - ant_offset_t old_brk = ctx->js->brk; - - for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { - if (!gc_stack_word_readable(addr)) continue; - uint64_t w; - memcpy(&w, (void *)addr, sizeof(w)); - - if (w <= NANBOX_PREFIX) continue; - - uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; - if (type > T_FFI) continue; - if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; - - ant_offset_t old_off = (ant_offset_t)(w & NANBOX_DATA_MASK); - if (old_off == 0 || old_off >= old_brk) continue; - if (!gc_stack_word_valid(ctx, type, old_off, old_brk)) continue; - - if (fwd_lookup(&ctx->fwd, old_off) != (ant_offset_t)~0) continue; - gc_update_val(ctx, w); - } -} - -__attribute__((noinline)) -static void gc_scan_stack_update(gc_ctx_t *ctx) { - jmp_buf jb; - if (setjmp(jb) != 0) return; - - volatile uint8_t sp_marker = 0; uintptr_t lo, hi; - if (!gc_get_stack_bounds((uintptr_t)ctx->js->cstk.base, (uintptr_t)&sp_marker, &lo, &hi)) return; - - ant_offset_t old_brk = ctx->js->brk; - - for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { - if (!gc_stack_word_readable(addr)) continue; - uint64_t w; - memcpy(&w, (void *)addr, sizeof(w)); - - if (w <= NANBOX_PREFIX) continue; - - uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; - if (type > T_FFI) continue; - if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; - - ant_offset_t old_off = (ant_offset_t)(w & NANBOX_DATA_MASK); - if (old_off == 0 || old_off >= old_brk) continue; - if (!gc_stack_word_valid(ctx, type, old_off, old_brk)) continue; - - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off == (ant_offset_t)~0 || new_off == old_off) continue; - - uint64_t updated = gc_mkval(type, new_off); - memcpy((void *)addr, &updated, sizeof(updated)); - } -} - -static void gc_scan_range_reserve(gc_ctx_t *ctx, uintptr_t lo, uintptr_t hi) { - ant_offset_t old_brk = ctx->js->brk; - for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { - if (!gc_stack_word_readable(addr)) continue; - uint64_t w; - memcpy(&w, (void *)addr, sizeof(w)); - if (w <= NANBOX_PREFIX) continue; - uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; - if (type > T_FFI) continue; - if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; - ant_offset_t old_off = (ant_offset_t)(w & NANBOX_DATA_MASK); - if (old_off == 0 || old_off >= old_brk) continue; - if (!gc_stack_word_valid(ctx, type, old_off, old_brk)) continue; - if (fwd_lookup(&ctx->fwd, old_off) != (ant_offset_t)~0) continue; - gc_update_val(ctx, w); - } -} - -static void gc_scan_range_update(gc_ctx_t *ctx, uintptr_t lo, uintptr_t hi) { - ant_offset_t old_brk = ctx->js->brk; - for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { - if (!gc_stack_word_readable(addr)) continue; - uint64_t w; - memcpy(&w, (void *)addr, sizeof(w)); - if (w <= NANBOX_PREFIX) continue; - uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; - if (type > T_FFI) continue; - if (!((1u << type) & GC_HEAP_TYPE_MASK)) continue; - ant_offset_t old_off = (ant_offset_t)(w & NANBOX_DATA_MASK); - if (old_off == 0 || old_off >= old_brk) continue; - if (!gc_stack_word_valid(ctx, type, old_off, old_brk)) continue; - ant_offset_t new_off = fwd_lookup(&ctx->fwd, old_off); - if (new_off == (ant_offset_t)~0 || new_off == old_off) continue; - uint64_t updated = gc_mkval(type, new_off); - memcpy((void *)addr, &updated, sizeof(updated)); - } -} - -static inline void gc_scan_mco_stack( - gc_ctx_t *ctx, mco_coro *mco, mco_coro *skip, - void (*scan)(gc_ctx_t *, uintptr_t, uintptr_t) -) { - if (!mco || mco == skip) return; - if (!mco->stack_base || mco->stack_size == 0) return; - uintptr_t lo = (uintptr_t)mco->stack_base; - uintptr_t hi = lo + mco->stack_size; - scan(ctx, lo, hi); -} - -static void gc_scan_other_stacks_reserve(gc_ctx_t *ctx) { - mco_coro *running = mco_running(); - if (!running) return; - - for (mco_coro *a = running->prev_co; a; a = a->prev_co) - gc_scan_mco_stack(ctx, a, running, gc_scan_range_reserve); - - for (coroutine_t *c = pending_coroutines.head; c; c = c->next) - gc_scan_mco_stack(ctx, c->mco, running, gc_scan_range_reserve); - - if (ctx->js->cstk.main_base && ctx->js->cstk.main_lo) { - uintptr_t lo, hi; - if (gc_get_stack_bounds( - (uintptr_t)ctx->js->cstk.main_base, - (uintptr_t)ctx->js->cstk.main_lo, &lo, &hi) - ) gc_scan_range_reserve(ctx, lo, hi); - } -} - -static void gc_scan_other_stacks_update(gc_ctx_t *ctx) { - mco_coro *running = mco_running(); - if (!running) return; - - for (mco_coro *a = running->prev_co; a; a = a->prev_co) - gc_scan_mco_stack(ctx, a, running, gc_scan_range_update); - - for (coroutine_t *c = pending_coroutines.head; c; c = c->next) - gc_scan_mco_stack(ctx, c->mco, running, gc_scan_range_update); - - if (ctx->js->cstk.main_base && ctx->js->cstk.main_lo) { - uintptr_t lo, hi; - if (gc_get_stack_bounds( - (uintptr_t)ctx->js->cstk.main_base, - (uintptr_t)ctx->js->cstk.main_lo, &lo, &hi) - ) gc_scan_range_update(ctx, lo, hi); - } -} - -static void gc_forward_func_cb(void *ctx_ptr, sv_func_t *func) { - gc_ctx_t *ctx = (gc_ctx_t *)ctx_ptr; - gc_update_func_constants(ctx, func, 0); -} - -static void gc_compact(ant_t *js) { - js->needs_gc = false; - - if (!js || js->brk == 0) return; - if (js->brk < 2 * 1024 * 1024) return; - - time_t now = time(NULL); - if (now != (time_t)-1 && gc_last_run_time != 0) { - double elapsed = difftime(now, gc_last_run_time); - double cooldown; - - if (js->brk > 64 * 1024 * 1024) cooldown = 0.5; - else if (js->brk > 16 * 1024 * 1024) cooldown = 1.0; - else if (js->brk > 4 * 1024 * 1024) cooldown = 2.0; - else cooldown = 4.0; - - if (elapsed >= 0.0 - && elapsed < cooldown - && js->gc_alloc_since < js->brk / 4 - ) return; - } - - if (now != (time_t)-1) gc_last_run_time = now; - size_t new_size = js->size; - - if (new_size > gc_scratch_size) { - if (gc_scratch_buf) gc_munmap(gc_scratch_buf, gc_scratch_size); - gc_scratch_buf = (uint8_t *)gc_mmap(new_size); - gc_scratch_size = gc_scratch_buf ? new_size : 0; - } - - if (!gc_scratch_buf) return; - uint8_t *new_mem = gc_scratch_buf; - - size_t bitmap_size = (js->brk / 8 + 7) / 8 + 1; - uint8_t *mark_bits = (uint8_t *)calloc(1, bitmap_size); - if (!mark_bits) return; - - size_t estimated_objs = js->brk / 64; - if (estimated_objs < 256) estimated_objs = 256; - - gc_ctx_t ctx; - ctx.js = js; - ctx.new_mem = new_mem; - ctx.new_brk = NANBOX_HEAP_OFFSET; - ctx.new_size = (ant_offset_t)new_size; - ctx.mark_bits = mark_bits; - - if (!fwd_init(&ctx.fwd, estimated_objs)) { - free(mark_bits); return; - } - - if (!work_init(&ctx.work, estimated_objs / 4 < 64 ? 64 : estimated_objs / 4)) { - fwd_free(&ctx.fwd); - free(mark_bits); return; - } - - ctx.failed = false; - gc_epoch_counter++; - - if (js->brk > 0) { - ant_offset_t header_at_0 = gc_loadoff(js->mem, 0); - if ((header_at_0 & 3) == T_OBJ) gc_reserve_object(&ctx, 0); - } - - js_gc_reserve_roots(js, - gc_fwd_off_callback, - gc_fwd_val_callback, - &ctx - ); gc_drain_work_queue(&ctx); - - gc_scan_stack_reserve(&ctx); - gc_scan_other_stacks_reserve(&ctx); - gc_drain_work_queue(&ctx); - - if (ctx.failed) { - free(mark_bits); - work_free(&ctx.work); - fwd_free(&ctx.fwd); return; - } - - js_gc_visit_frame_funcs(js, gc_forward_func_cb, &ctx); - gc_drain_work_queue(&ctx); - - js_gc_update_roots(js, - gc_apply_off_callback, - gc_weak_off_callback, - gc_apply_val_callback, - &ctx); - - gc_scan_stack_update(&ctx); - gc_scan_other_stacks_update(&ctx); - memcpy(js->mem, new_mem, ctx.new_brk); - js->brk = ctx.new_brk; - - free(mark_bits); - work_free(&ctx.work); - fwd_free(&ctx.fwd); - - if (gc_scratch_buf && gc_scratch_size > 0) { - RELEASE_PAGES(gc_scratch_buf, gc_scratch_size); - } - - size_t new_brk = ctx.new_brk; - size_t old_size = js->size; - - if (new_brk < old_size * 3 / 4 && old_size > ARENA_GROW_INCREMENT) { - size_t target = ((new_brk * 3 / 2 + ARENA_GROW_INCREMENT - 1) / ARENA_GROW_INCREMENT) * ARENA_GROW_INCREMENT; - if (target < ARENA_GROW_INCREMENT) target = ARENA_GROW_INCREMENT; - if (target < old_size) { ant_arena_decommit(js->mem, old_size, target); js->size = (ant_offset_t)target; } - } -} - -void js_gc_maybe(ant_t *js) { -#ifdef ANT_JIT - if (js->jit_active_depth > 0) return; -#endif - ant_offset_t thresh = js->brk / 4; - - ant_offset_t min_thresh = gc_throttled - ? 8 * 1024 * 1024 - : 2 * 1024 * 1024; - - ant_offset_t max_thresh = gc_throttled - ? 64 * 1024 * 1024 - : 16 * 1024 * 1024; - - if (thresh < min_thresh) thresh = min_thresh; - if (thresh > max_thresh) thresh = max_thresh; - - if (js->gc_alloc_since > thresh || js->needs_gc) { - gc_compact(js); - js->gc_alloc_since = 0; - } -} diff --git a/src/gc/gc.c b/src/gc/gc.c new file mode 100644 index 0000000..05c5a33 --- /dev/null +++ b/src/gc/gc.c @@ -0,0 +1,160 @@ +#include +#include +#include +#include "shapes.h" + +#include "gc/objects.h" +#include "gc/refs.h" +#include "gc/strings.h" +#include "gc/ropes.h" + +bool gc_disabled = false; + +static size_t gc_tick = 0; +static uint64_t gc_last_run_ms = 0; + +static size_t gc_nursery_threshold = GC_NURSERY_THRESHOLD; +static uint32_t gc_major_every_n = GC_MAJOR_EVERY_N_MINOR; + +static uint32_t gc_minor_surv_ewma = 128; +static uint32_t gc_major_recl_ewma = 26; + +static uint64_t gc_now_ms(void) { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (uint64_t)ts.tv_sec * 1000ULL + (uint64_t)ts.tv_nsec / 1000000ULL; +} + +static void gc_adapt_nursery(size_t young_before, size_t survivors) { + if (young_before == 0) return; + uint32_t rate = (uint32_t)((survivors * 256) / young_before); + gc_minor_surv_ewma = (gc_minor_surv_ewma * 3 + rate) >> 2; + if (gc_minor_surv_ewma < 64 && gc_nursery_threshold > GC_NURSERY_THRESHOLD / 2) + gc_nursery_threshold -= gc_nursery_threshold / 4; + else if (gc_nursery_threshold < GC_NURSERY_THRESHOLD) + gc_nursery_threshold = GC_NURSERY_THRESHOLD; +} + +static void gc_adapt_major_interval(size_t live_before, size_t live_after) { + if (live_before == 0) return; + size_t freed = live_before > live_after ? live_before - live_after : 0; + uint32_t rate = (uint32_t)((freed * 256) / live_before); + gc_major_recl_ewma = (gc_major_recl_ewma * 3 + rate) >> 2; + + bool gen_ineffective = gc_minor_surv_ewma > 192; // >75% nursery survival + bool high_reclaim = gc_major_recl_ewma > 51; // >20% old-gen freed + bool low_reclaim = gc_major_recl_ewma < 13; // < 5% old-gen freed + + if ((gen_ineffective && !low_reclaim) || high_reclaim) { + if (gc_major_every_n > 2) gc_major_every_n--; + } else if (!gen_ineffective && low_reclaim) { + if (gc_major_every_n < GC_MAJOR_EVERY_N_MINOR * 4) gc_major_every_n++; + } +} + +static void gc_mark_str(ant_t *js, ant_value_t v) { + if (v <= NANBOX_PREFIX) return; + uint8_t t = (v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; + + if (t != T_STR) return; + uintptr_t data = (uintptr_t)(v & NANBOX_DATA_MASK); + + if (data & 1ULL) { + ant_rope_heap_t *rope = (ant_rope_heap_t *)(data & ~1ULL); + + if (!rope) return; + if (!gc_ropes_mark(rope)) return; + + gc_mark_str(js, rope->left); + gc_mark_str(js, rope->right); + gc_mark_str(js, rope->cached); + + } else if (data) gc_strings_mark(js, (const void *)data); +} + +void gc_run(ant_t *js) { + if (__builtin_expect(gc_disabled, 0)) return; + size_t live_before = js->obj_arena.live_count; + + js->prop_refs_len = 0; + gc_refs_shrink(js); + + gc_strings_begin(js); + gc_ropes_begin(js); + gc_objects_run(js, gc_mark_str); + ant_ic_epoch_bump(); + + gc_strings_sweep(js); + gc_ropes_sweep(js); + + js->gc_last_live = js->obj_arena.live_count; + js->old_live_count = js->obj_arena.live_count; /* after major GC all live = old */ + js->minor_gc_count = 0; + + ant_pool_stats_t pool_stats = js_class_pool_stats(&js->pool.string); + js->gc_pool_last_live = pool_stats.used; + js->gc_pool_alloc = 0; + + gc_adapt_major_interval(live_before, js->obj_arena.live_count); + gc_last_run_ms = gc_now_ms(); +} + +void gc_run_minor(ant_t *js) { + if (__builtin_expect(gc_disabled, 0)) return; + + size_t old_before = js->old_live_count; + size_t live_before = js->obj_arena.live_count; + size_t young_before = live_before > old_before ? live_before - old_before : 0; + + gc_objects_run_minor(js, NULL); + ant_ic_epoch_bump(); + + js->gc_last_live = js->obj_arena.live_count; + js->old_live_count = js->obj_arena.live_count; + js->minor_gc_count++; + + size_t survivors = js->obj_arena.live_count > old_before + ? js->obj_arena.live_count - old_before : 0; + + gc_adapt_nursery(young_before, survivors); + gc_last_run_ms = gc_now_ms(); +} + +void gc_maybe(ant_t *js) { + if (__builtin_expect(gc_disabled, 0)) return; + if (++gc_tick < GC_MIN_TICK) return; + size_t live = js->obj_arena.live_count; + + size_t young_count = live > js->old_live_count ? live - js->old_live_count : 0; + if (young_count >= gc_nursery_threshold) { + gc_tick = 0; + gc_run_minor(js); + if (js->minor_gc_count >= gc_major_every_n) { + js->minor_gc_count = 0; gc_run(js); + } + return; + } + + size_t threshold = GC_HEAP_GROWTH(js->gc_last_live); + if (threshold < 2048) threshold = 2048; + + if (live >= threshold) { + gc_tick = 0; gc_run(js); + return; + } + + if (gc_tick < 8192) return; + + if (young_count == 0 && js->gc_pool_alloc == 0) { + gc_tick = 0; + return; + } + + if (gc_now_ms() - gc_last_run_ms < GC_FORCE_INTERVAL_MS) { + gc_tick = 0; + return; + } + + gc_tick = 0; + gc_run(js); +} diff --git a/src/gc/objects.c b/src/gc/objects.c new file mode 100644 index 0000000..756dd7d --- /dev/null +++ b/src/gc/objects.c @@ -0,0 +1,743 @@ +#include "internal.h" +#include "gc.h" +#include "gc/objects.h" +#include "gc/roots.h" +#include "gc/modules.h" +#include "sugar.h" +#include "silver/engine.h" +#include "shapes.h" +#include "runtime.h" +#include "modules/collections.h" +#include "modules/regex.h" + +#include +#include +#include +#include +#include + +#define MCO_API extern +#include + +#if defined(__has_feature) +#if __has_feature(address_sanitizer) +#include +#define ANT_HAS_ASAN 1 +#endif +#endif + +#ifndef ANT_HAS_ASAN +#define ANT_HAS_ASAN 0 +#endif + +static inline bool gc_stack_word_readable(uintptr_t addr) { +#if ANT_HAS_ASAN + return __asan_region_is_poisoned((void *)addr, sizeof(uint64_t)) == NULL; +#else + return true; +#endif +} + +static inline bool gc_get_stack_bounds( + uintptr_t base, uintptr_t sp, uintptr_t *lo, uintptr_t *hi +) { + if (base == 0 || sp == 0) return false; + uintptr_t minp = (sp < base) ? sp : base; + uintptr_t maxp = (sp < base) ? base : sp; + + uintptr_t aligned_lo = (minp + sizeof(uint64_t) - 1u) & ~(sizeof(uint64_t) - 1u); + uintptr_t aligned_hi = maxp & ~(sizeof(uint64_t) - 1u); + + if (aligned_lo >= aligned_hi) return false; + *lo = aligned_lo; + *hi = aligned_hi; + return true; +} + +static gc_func_mark_profile_t g_gc_func_mark_profile = {0}; +static uint32_t g_gc_func_mark_profile_depth = 0; +static uint64_t g_gc_func_mark_profile_start_ns = 0; + +static gc_str_mark_fn g_str_mark = NULL; +static ant_object_t *g_pending_promises = NULL; + +static uint64_t gc_epoch = 0; +static uint8_t gc_obj_epoch = 0; +static bool g_minor_gc = false; + +static uint64_t gc_now_ns(void) { + struct timeval tv; + gettimeofday(&tv, NULL); + return (uint64_t)tv.tv_sec * 1000000000ULL + (uint64_t)tv.tv_usec * 1000ULL; +} + +void gc_func_mark_profile_enable(bool enabled) { + g_gc_func_mark_profile.enabled = enabled; +} + +void gc_func_mark_profile_reset(void) { + bool enabled = g_gc_func_mark_profile.enabled; + g_gc_func_mark_profile = (gc_func_mark_profile_t){ .enabled = enabled }; + g_gc_func_mark_profile_depth = 0; + g_gc_func_mark_profile_start_ns = 0; +} + +gc_func_mark_profile_t gc_func_mark_profile_get(void) { + return g_gc_func_mark_profile; +} + +bool gc_obj_is_marked(const ant_object_t *obj) { + return obj && obj->mark_epoch == gc_obj_epoch; +} + +void gc_root_pending_promise(ant_object_t *obj) { + if (!obj || obj->gc_pending_rooted) return; + obj->gc_pending_rooted = true; + obj->gc_pending_next = g_pending_promises; + g_pending_promises = obj; +} + +void gc_unroot_pending_promise(ant_object_t *obj) { + if (!obj || !obj->gc_pending_rooted) return; + obj->gc_pending_rooted = false; + ant_object_t **pp = &g_pending_promises; + while (*pp) { + if (*pp == obj) { *pp = obj->gc_pending_next; break; } + pp = &(*pp)->gc_pending_next; + } + obj->gc_pending_next = NULL; +} + +void gc_remember_add(ant_t *js, ant_object_t *obj) { + if (!obj || obj->in_remember_set) return; + if (js->remember_set_len >= js->remember_set_cap) { + size_t new_cap = js->remember_set_cap ? js->remember_set_cap * 2 : 64; + ant_object_t **ns = realloc(js->remember_set, new_cap * sizeof(*ns)); + if (!ns) return; + js->remember_set = ns; + js->remember_set_cap = new_cap; + } + obj->in_remember_set = 1; + js->remember_set[js->remember_set_len++] = obj; +} + +typedef struct { + ant_value_t onFulfilled; + ant_value_t onRejected; + ant_value_t nextPromise; +} promise_handler_t; + +#define GC_MARK_STACK_INIT 4096 +void gc_mark_value(ant_t *js, ant_value_t v); + +static ant_object_t **gc_mark_stack = NULL; +static size_t gc_mark_sp = 0; +static size_t gc_mark_cap = 0; + +static void gc_mark_stack_push(ant_object_t *obj) { + if (gc_mark_sp >= gc_mark_cap) { + size_t new_cap = gc_mark_cap ? gc_mark_cap * 2 : GC_MARK_STACK_INIT; + ant_object_t **ns = realloc(gc_mark_stack, new_cap * sizeof(*ns)); + if (!ns) return; + gc_mark_stack = ns; + gc_mark_cap = new_cap; + } + gc_mark_stack[gc_mark_sp++] = obj; +} + +static inline void gc_grey_obj(ant_t *js, ant_object_t *obj) { + if (!obj || !fixed_arena_contains(&js->obj_arena, obj)) return; + if (obj->mark_epoch == gc_obj_epoch || obj->mark_epoch == ANT_GC_DEAD) return; + if (g_minor_gc && obj->generation == 1) return; + obj->mark_epoch = gc_obj_epoch; + gc_mark_stack_push(obj); +} + +static void gc_mark_func(ant_t *js, sv_func_t *func) { + if (!func) return; + if (func->gc_epoch == gc_epoch) return; + + bool prof = __builtin_expect(g_gc_func_mark_profile.enabled, 0); + if (prof) { + if (g_gc_func_mark_profile_depth++ == 0) g_gc_func_mark_profile_start_ns = gc_now_ns(); + g_gc_func_mark_profile.func_visits++; + g_gc_func_mark_profile.child_edges += (uint64_t)func->child_func_count; + g_gc_func_mark_profile.const_slots += (uint64_t)func->gc_const_slot_count; + } + + func->gc_epoch = gc_epoch; + + for (int i = 0; i < func->child_func_count; i++) + gc_mark_func(js, func->child_funcs[i]); + + for (int i = 0; i < func->obj_site_count; i++) { + if (func->obj_sites) ant_gc_shapes_mark(func->obj_sites[i].shared_shape); + } + + for (int i = 0; i < func->gc_const_slot_count; i++) { + uint32_t idx = func->gc_const_slots[i]; + ant_value_t v = func->constants[idx]; + gc_mark_value(js, v); + } + + if (prof && --g_gc_func_mark_profile_depth == 0) + g_gc_func_mark_profile.time_ns += gc_now_ns() - g_gc_func_mark_profile_start_ns; +} + +static void gc_mark_closure(ant_t *js, sv_closure_t *c) { + if (!c) return; + if (c->gc_epoch == gc_epoch) return; + + c->gc_epoch = gc_epoch; + gc_mark_func(js, c->func); + gc_mark_value(js, c->func_obj); + gc_mark_value(js, c->bound_this); + gc_mark_value(js, c->bound_args); + gc_mark_value(js, c->super_val); + + if (c->upvalues && c->func) { + for (int i = 0; i < c->func->upvalue_count; i++) { + sv_upvalue_t *uv = c->upvalues[i]; + if (!uv) continue; + uv->gc_epoch = gc_epoch; + if (uv->location == &uv->closed) gc_mark_value(js, uv->closed); + }} + + if (c->bound_argv) { + for (int i = 0; i < c->bound_argc; i++) gc_mark_value(js, c->bound_argv[i]); + } +} + +void gc_mark_value(ant_t *js, ant_value_t v) { + if (v <= NANBOX_PREFIX) return; + uint8_t t = (v >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; + + if (t == T_FUNC) { + gc_mark_closure(js, (sv_closure_t *)(uintptr_t)(v & NANBOX_DATA_MASK)); + return; + } + + if (t == T_STR && g_str_mark) { + g_str_mark(js, v); + return; + } + + if (!((1u << t) & GC_OBJ_TYPE_MASK)) return; + ant_object_t *obj = (ant_object_t *)(uintptr_t)(v & NANBOX_DATA_MASK); + + if (!obj) return; + gc_grey_obj(js, obj); +} + +static void gc_scan_obj(ant_t *js, ant_object_t *obj) { + ant_gc_shapes_mark(obj->shape); + gc_mark_value(js, obj->proto); + + if (obj->type_tag != T_ARR) gc_mark_value(js, obj->u.data.value); + + if (obj->type_tag == T_MAP) { + map_entry_t **head = (map_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + map_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { gc_mark_value(js, e->value); } + } + } else if (obj->type_tag == T_SET) { + set_entry_t **head = (set_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + set_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { gc_mark_value(js, e->value); } + } + } + + if (obj->shape) { + uint32_t count = ant_shape_count(obj->shape); + for (uint32_t i = 0; i < count && i < obj->prop_count; i++) + gc_mark_value(js, ant_object_prop_get_unchecked(obj, i)); + for (uint32_t i = 0; i < count; i++) { + const ant_shape_prop_t *prop = ant_shape_prop_at(obj->shape, i); + if (prop && prop->has_getter) gc_mark_value(js, prop->getter); + if (prop && prop->has_setter) gc_mark_value(js, prop->setter); + } + } + + if (obj->extra_slots) { + ant_extra_slot_t *entries = (ant_extra_slot_t *)obj->extra_slots; + for (uint8_t i = 0; i < obj->extra_count; i++) gc_mark_value(js, entries[i].value); + } + + if (obj->type_tag == T_ARR && obj->u.array.data) { + uint32_t n = obj->u.array.len < obj->u.array.cap ? obj->u.array.len : obj->u.array.cap; + for (uint32_t i = 0; i < n; i++) gc_mark_value(js, obj->u.array.data[i]); + } + + if (obj->promise_state) { + gc_mark_value(js, obj->promise_state->value); + gc_mark_value(js, obj->promise_state->trigger_parent); + if (obj->promise_state->handlers) { + promise_handler_t *h = NULL; + while ((h = (promise_handler_t *)utarray_next(obj->promise_state->handlers, h))) { + gc_mark_value(js, h->onFulfilled); + gc_mark_value(js, h->onRejected); + gc_mark_value(js, h->nextPromise); + }} + } + + if (obj->proxy_state) { + gc_mark_value(js, obj->proxy_state->target); + gc_mark_value(js, obj->proxy_state->handler); + } +} + +static void gc_drain_mark_stack(ant_t *js) { +while (gc_mark_sp > 0) { + ant_object_t *obj = gc_mark_stack[--gc_mark_sp]; + gc_scan_obj(js, obj); +}} + +static void gc_scan_vm_stack(ant_t *js, sv_vm_t *vm) { + if (!vm) return; + + for (int i = 0; i < vm->sp; i++) + gc_mark_value(js, vm->stack[i]); + + for (int f = 0; f <= vm->fp; f++) { + sv_frame_t *frame = &vm->frames[f]; + gc_mark_func(js, frame->func); + gc_mark_value(js, frame->callee); + gc_mark_value(js, frame->this); + gc_mark_value(js, frame->new_target); + gc_mark_value(js, frame->super_val); + gc_mark_value(js, frame->with_obj); + gc_mark_value(js, frame->completion.value); + } + + for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) { + uv->gc_epoch = gc_epoch; + if (uv->location == &uv->closed) gc_mark_value(js, uv->closed); + } + + for (int f = 0; f <= vm->fp; f++) { + sv_frame_t *frame = &vm->frames[f]; + if (!frame->upvalues) continue; + for (int j = 0; j < frame->upvalue_count; j++) { + sv_upvalue_t *uv = frame->upvalues[j]; + if (!uv) continue; + uv->gc_epoch = gc_epoch; + if (uv->location == &uv->closed) gc_mark_value(js, uv->closed); + }} +} + +static void gc_scan_range(ant_t *js, uintptr_t lo, uintptr_t hi) { + for (uintptr_t addr = lo; addr < hi; addr += sizeof(uint64_t)) { + if (!gc_stack_word_readable(addr)) continue; + uint64_t w; + memcpy(&w, (void *)addr, sizeof(w)); + + ant_object_t *raw_obj = (ant_object_t *)(uintptr_t)w; + if (fixed_arena_contains(&js->obj_arena, raw_obj)) + gc_grey_obj(js, raw_obj); + + sv_closure_t *raw_closure = (sv_closure_t *)(uintptr_t)w; + if (fixed_arena_contains(&js->closure_arena, raw_closure)) + gc_mark_closure(js, raw_closure); + + if (w <= NANBOX_PREFIX) continue; + uint8_t type = (w >> NANBOX_TYPE_SHIFT) & NANBOX_TYPE_MASK; + + if ((1u << type) & GC_OBJ_TYPE_MASK) { + ant_object_t *obj = (ant_object_t *)(uintptr_t)(w & NANBOX_DATA_MASK); + if (obj) gc_grey_obj(js, obj); + } + + if (type == T_FUNC) { + sv_closure_t *c = (sv_closure_t *)(uintptr_t)(w & NANBOX_DATA_MASK); + if (c && fixed_arena_contains(&js->closure_arena, c)) gc_mark_closure(js, c); + } + + if (type == T_STR && g_str_mark) g_str_mark(js, w); + } +} + +__attribute__((noinline)) +static void gc_scan_current_stack(ant_t *js) { + jmp_buf jb; + if (setjmp(jb) != 0) return; + volatile uint8_t sp_marker = 0; + + mco_coro *running = mco_running(); + uintptr_t base; + + if (running && running->stack_base && running->stack_size > 0) { + base = (uintptr_t)running->stack_base + running->stack_size; + } else base = (uintptr_t)js->cstk.base; + + uintptr_t lo, hi; + if (!gc_get_stack_bounds(base, (uintptr_t)&sp_marker, &lo, &hi)) return; + gc_scan_range(js, lo, hi); +} + +static void gc_scan_mco_stack(ant_t *js, mco_coro *mco, mco_coro *skip) { + if (!mco || mco == skip) return; + if (!mco->stack_base || mco->stack_size == 0) return; + uintptr_t lo = (uintptr_t)mco->stack_base; + uintptr_t hi = lo + mco->stack_size; + gc_scan_range(js, lo, hi); +} + +static void gc_scan_other_stacks(ant_t *js) { + mco_coro *running = mco_running(); + + if (running) { + for (mco_coro *a = running->prev_co; a; a = a->prev_co) + gc_scan_mco_stack(js, a, running); + } + + for (coroutine_t *c = pending_coroutines.head; c; c = c->next) + gc_scan_mco_stack(js, c->mco, running); + + if (js->cstk.main_base && js->cstk.main_lo) { + uintptr_t lo, hi; + if (gc_get_stack_bounds( + (uintptr_t)js->cstk.main_base, + (uintptr_t)js->cstk.main_lo, &lo, &hi)) + gc_scan_range(js, lo, hi); + } +} + +static void gc_mark_roots(ant_t *js) { + gc_scan_vm_stack(js, js->vm); + + for (coroutine_t *c = pending_coroutines.head; c; c = c->next) { + gc_scan_vm_stack(js, c->sv_vm); + gc_mark_value(js, c->this_val); + gc_mark_value(js, c->async_func); + gc_mark_value(js, c->async_promise); + gc_mark_value(js, c->awaited_promise); + gc_mark_value(js, c->result); + gc_mark_value(js, c->yield_value); + gc_mark_value(js, c->super_val); + gc_mark_value(js, c->new_target); + } + + gc_mark_value(js, js->global); + gc_mark_value(js, js->object); + gc_mark_value(js, js->this_val); + gc_mark_value(js, js->new_target); + gc_mark_value(js, js->current_func); + gc_mark_value(js, js->thrown_value); + gc_mark_value(js, js->thrown_stack); + gc_mark_value(js, js->length_str); + + if (rt && rt->js == js) + gc_mark_value(js, rt->ant_obj); + + for (ant_module_t *ctx = js->module; ctx; ctx = ctx->prev) { + gc_mark_value(js, ctx->module_ns); + gc_mark_value(js, ctx->import_meta); + gc_mark_value(js, ctx->prev_import_meta_prop); + } + + gc_visit_roots(js, gc_mark_value); + gc_mark_timers(js, gc_mark_value); + gc_mark_ffi(js, gc_mark_value); + gc_mark_fetch(js, gc_mark_value); + gc_mark_fs(js, gc_mark_value); + gc_mark_child_process(js, gc_mark_value); + gc_mark_readline(js, gc_mark_value); + gc_mark_process(js, gc_mark_value); + gc_mark_navigator(js, gc_mark_value); + gc_mark_server(js, gc_mark_value); + gc_mark_events(js, gc_mark_value); + gc_mark_lmdb(js, gc_mark_value); + gc_mark_symbols(js, gc_mark_value); + gc_mark_esm(js, gc_mark_value); + gc_mark_worker_threads(js, gc_mark_value); + + for ( + ant_object_t *obj = g_pending_promises; + obj; obj = obj->gc_pending_next + ) gc_grey_obj(js, obj); + + gc_scan_current_stack(js); + gc_scan_other_stacks(js); + + if (!g_minor_gc) { + for (ant_object_t *obj = js->permanent_objects; obj; obj = obj->next) + gc_scan_obj(js, obj); + } + + gc_drain_mark_stack(js); +} + +void gc_object_free(ant_t *js, ant_object_t *obj) { + if (!obj) return; + obj->mark_epoch = ANT_GC_DEAD; + + if (obj->shape) { + ant_shape_release(obj->shape); + obj->shape = NULL; + } + + if (obj->extra_slots) { + free(obj->extra_slots); + obj->extra_slots = NULL; + } + + if (obj->gc_pending_rooted) + gc_unroot_pending_promise(obj); + + if (obj->promise_state) { + if (obj->promise_state->handlers) + utarray_free(obj->promise_state->handlers); + free(obj->promise_state); + obj->promise_state = NULL; + } + + if (obj->proxy_state) { + free(obj->proxy_state); + obj->proxy_state = NULL; + } + + if (obj->type_tag == T_ARR && obj->u.array.data) { + free(obj->u.array.data); + obj->u.array.data = NULL; + } + + switch (obj->type_tag) { + case T_MAP: { + map_entry_t **head = (map_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + map_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { HASH_DEL(*head, e); free(e->key); free(e); } + free(head); + } + break; + } + case T_SET: { + set_entry_t **head = (set_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + set_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { HASH_DEL(*head, e); free(e->key); free(e); } + free(head); + } + break; + } + case T_WEAKMAP: { + weakmap_entry_t **head = (weakmap_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + weakmap_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { HASH_DEL(*head, e); free(e); } + free(head); + } + break; + } + case T_WEAKSET: { + weakset_entry_t **head = (weakset_entry_t **)(uintptr_t)js_getnum(obj->u.data.value); + if (head) { + weakset_entry_t *e, *tmp; + HASH_ITER(hh, *head, e, tmp) { HASH_DEL(*head, e); free(e); } + free(head); + } + break; + } + default: break; + } + + free(obj->overflow_prop); + obj->overflow_prop = NULL; + free((void *)obj->exotic_ops); + obj->exotic_ops = NULL; + fixed_arena_free_elem(&js->obj_arena, obj); +} + +static void gc_sweep_young(ant_t *js) { + ant_object_t **pp = &js->objects; + while (*pp) { + ant_object_t *obj = *pp; + if (obj->mark_epoch == gc_obj_epoch) pp = &obj->next; else { + *pp = obj->next; + gc_object_free(js, obj); + }} +} + +static void gc_promote_survivors(ant_t *js) { + ant_object_t *obj = js->objects; + while (obj) { + ant_object_t *next = obj->next; + obj->generation = 1; + obj->next = js->objects_old; + js->objects_old = obj; + obj = next; + } + js->objects = NULL; +} + +static void gc_sweep(ant_t *js) { + ant_object_t **pp = &js->objects; + while (*pp) { + ant_object_t *obj = *pp; + if (obj->mark_epoch == gc_obj_epoch) pp = &obj->next; else { + *pp = obj->next; + gc_object_free(js, obj); + }} + + pp = &js->objects_old; + while (*pp) { + ant_object_t *obj = *pp; + if (obj->mark_epoch == gc_obj_epoch) pp = &obj->next; else { + *pp = obj->next; + gc_object_free(js, obj); + }} +} + +void gc_pin_existing_objects(ant_t *js) { + if (!js) return; + + ant_object_t *tail = NULL; + for (ant_object_t *obj = js->objects; obj; obj = obj->next) { + obj->gc_permanent = 1; + obj->generation = 1; + tail = obj; + } + + if (tail) { + tail->next = js->permanent_objects; + js->permanent_objects = js->objects; + js->objects = NULL; + } + + tail = NULL; + for (ant_object_t *obj = js->objects_old; obj; obj = obj->next) { + obj->gc_permanent = 1; + tail = obj; + } + + if (tail) { + tail->next = js->permanent_objects; + js->permanent_objects = js->objects_old; + js->objects_old = NULL; + } +} + +void gc_objects_run(ant_t *js, gc_str_mark_fn str_mark) { + if (!js) return; + + g_str_mark = str_mark; + if (g_gc_func_mark_profile.enabled) g_gc_func_mark_profile.collections++; + + gc_epoch++; + if (gc_epoch == 0) gc_epoch = 1; + + gc_obj_epoch = (uint8_t)(gc_obj_epoch + 1u); + if (gc_obj_epoch == 0 || gc_obj_epoch == ANT_GC_DEAD) gc_obj_epoch = 1; + + ant_gc_shapes_begin(); + for (size_t i = 0; i < js->remember_set_len; i++) + js->remember_set[i]->in_remember_set = 0; + js->remember_set_len = 0; + + if (js->remember_set_cap > 512) { + ant_object_t **ns = realloc(js->remember_set, 256 * sizeof(*ns)); + if (ns) { js->remember_set = ns; js->remember_set_cap = 256; } + } + + gc_mark_roots(js); + gc_sweep_regex_cache(); + gc_sweep(js); + ant_gc_shapes_sweep(); + ant_ic_epoch_bump(); + gc_promote_survivors(js); + + + ant_fixed_arena_t *ca = &js->closure_arena; + ca->free_list = NULL; + ca->live_count = 0; + for (size_t off = 0; off < ca->watermark; off += ca->elem_size) { + sv_closure_t *c = (sv_closure_t *)(ca->base + off); + if (c->gc_epoch == gc_epoch) ca->live_count++; + else { + free(c->upvalues); + c->upvalues = NULL; + free(c->bound_argv); + c->bound_argv = NULL; + *(void **)c = ca->free_list; + ca->free_list = c; + } + } + + ant_fixed_arena_t *ua = &js->upvalue_arena; + ua->free_list = NULL; + ua->live_count = 0; + for (size_t off = 0; off < ua->watermark; off += ua->elem_size) { + uint8_t *slot = ua->base + off; + uint64_t epoch; + memcpy(&epoch, slot + ua->epoch_offset, sizeof(epoch)); + if (epoch == gc_epoch) ua->live_count++; + else { + *(void **)slot = ua->free_list; + ua->free_list = slot; + } + } + + ant_fixed_arena_t *oa = &js->obj_arena; + size_t new_wm = 0; + + for (size_t off = oa->watermark; off >= oa->elem_size; off -= oa->elem_size) { + ant_object_t *slot = (ant_object_t *)(oa->base + off - oa->elem_size); + if (slot->mark_epoch != ANT_GC_DEAD) { new_wm = off; break; } + } + + if (new_wm < oa->watermark) { + oa->free_list = NULL; + + for (size_t off = 0; off < new_wm; off += oa->elem_size) { + ant_object_t *slot = (ant_object_t *)(oa->base + off); + if (slot->mark_epoch == ANT_GC_DEAD) { + *(void **)slot = oa->free_list; + oa->free_list = slot; + }} + + ant_arena_decommit(oa->base, oa->committed, new_wm); + oa->committed = new_wm; + oa->watermark = new_wm; + } + + if (gc_mark_cap > GC_MARK_STACK_INIT) { + size_t target = js->obj_arena.live_count * 2; + if (target < GC_MARK_STACK_INIT) target = GC_MARK_STACK_INIT; + if (target < gc_mark_cap / 2) { + ant_object_t **ns = realloc(gc_mark_stack, target * sizeof(*ns)); + if (ns) { gc_mark_stack = ns; gc_mark_cap = target; } + } + } +} + +void gc_objects_run_minor(ant_t *js, gc_str_mark_fn str_mark) { + if (!js) return; + + g_str_mark = str_mark; + gc_epoch++; + + if (gc_epoch == 0) gc_epoch = 1; + gc_obj_epoch = (uint8_t)(gc_obj_epoch + 1u); + + if (gc_obj_epoch == 0 || gc_obj_epoch == ANT_GC_DEAD) gc_obj_epoch = 1; + g_minor_gc = true; + + for (size_t i = 0; i < js->remember_set_len; i++) gc_scan_obj(js, js->remember_set[i]); + for (size_t i = 0; i < js->remember_set_len; i++) js->remember_set[i]->in_remember_set = 0; + + js->remember_set_len = 0; + gc_mark_roots(js); + g_minor_gc = false; + + gc_sweep_regex_cache(); + gc_sweep_young(js); + gc_promote_survivors(js); + + // will NOT sweep closure/upvalue arenas here. old closures stored as T_FUNC + // property values on old objects are not scanned during minor GC (old objects + // are pre-marked but not traversed unless in the remember set), so their + // gc_epoch would not be updated and they would be incorrectly freed. + // closure/upvalue arenas are only swept on major GC `gc_objects_run` +} diff --git a/src/gc/refs.c b/src/gc/refs.c new file mode 100644 index 0000000..cbef646 --- /dev/null +++ b/src/gc/refs.c @@ -0,0 +1,17 @@ +#include "internal.h" +#include "gc/refs.h" + +#include + +void gc_refs_shrink(ant_t *js) { + if (!js || js->prop_refs_cap <= 512) return; + if (js->prop_refs_len >= js->prop_refs_cap / 4) return; + + ant_offset_t shrunk = js->prop_refs_len < 256 ? 256 : js->prop_refs_len * 2; + ant_prop_ref_t *next = realloc(js->prop_refs, sizeof(*next) * shrunk); + + if (next) { + js->prop_refs = next; + js->prop_refs_cap = shrunk; + } +} diff --git a/src/gc/roots.c b/src/gc/roots.c new file mode 100644 index 0000000..df97251 --- /dev/null +++ b/src/gc/roots.c @@ -0,0 +1,49 @@ +#include "gc/roots.h" +#include "internal.h" + +#include +#include + +static ant_value_t *g_roots[GC_MAX_STATIC_ROOTS]; +static size_t g_root_count = 0; + +void gc_register_root(ant_value_t *slot) { + if (g_root_count < GC_MAX_STATIC_ROOTS) + g_roots[g_root_count++] = slot; +} + +size_t gc_root_scope(ant_t *js) { + return js ? js->c_root_count : 0; +} + +bool gc_push_root(ant_t *js, ant_value_t *slot) { + if (!js || !slot) return false; + + if (js->c_root_count >= js->c_root_cap) { + size_t new_cap = js->c_root_cap ? js->c_root_cap * 2 : 64; + ant_value_t **next = realloc(js->c_roots, new_cap * sizeof(*next)); + if (!next) return false; + js->c_roots = next; + js->c_root_cap = new_cap; + } + + js->c_roots[js->c_root_count++] = slot; + return true; +} + +void gc_pop_roots(ant_t *js, size_t mark) { + if (!js) return; + js->c_root_count = (mark <= js->c_root_count) ? mark : 0; +} + +void gc_visit_roots(ant_t *js, gc_root_visitor_t visitor) { + for (size_t i = 0; i < g_root_count; i++) { + if (g_roots[i] && *g_roots[i]) visitor(js, *g_roots[i]); + } + + if (!js) return; + for (size_t i = 0; i < js->c_root_count; i++) { + ant_value_t *slot = js->c_roots[i]; + if (slot && *slot) visitor(js, *slot); + } +} diff --git a/src/gc/ropes.c b/src/gc/ropes.c new file mode 100644 index 0000000..df7814c --- /dev/null +++ b/src/gc/ropes.c @@ -0,0 +1,109 @@ +#include "internal.h" +#include "gc/ropes.h" +#include "pool.h" + +#include +#include +#include + +typedef struct { + uintptr_t base; + uintptr_t end; + ant_pool_block_t *block; + bool has_live; +} gc_rope_mark_t; + +static gc_rope_mark_t *g_rope_marks = NULL; +static int g_rope_mark_count = 0; +static int g_rope_mark_cap = 0; + +static int rope_mark_cmp(const void *a, const void *b) { + const gc_rope_mark_t *ma = (const gc_rope_mark_t *)a; + const gc_rope_mark_t *mb = (const gc_rope_mark_t *)b; + if (ma->base < mb->base) return -1; + if (ma->base > mb->base) return 1; + return 0; +} + +void gc_ropes_begin(ant_t *js) { + g_rope_mark_count = 0; + + for (ant_pool_block_t *b = js->pool.rope.head; b; b = b->next) { + if (b->used == 0) continue; + + if (g_rope_mark_count >= g_rope_mark_cap) { + int cap = g_rope_mark_cap ? g_rope_mark_cap * 2 : 32; + g_rope_marks = realloc(g_rope_marks, (size_t)cap * sizeof(gc_rope_mark_t)); + g_rope_mark_cap = cap; + } + + gc_rope_mark_t *m = &g_rope_marks[g_rope_mark_count++]; + m->base = (uintptr_t)b->data; + m->end = m->base + b->used; + m->block = b; + m->has_live = false; + } + + if (g_rope_mark_count > 1) + qsort(g_rope_marks, (size_t)g_rope_mark_count, sizeof(gc_rope_mark_t), rope_mark_cmp); +} + +bool gc_ropes_mark(const void *ptr) { + if (!ptr || g_rope_mark_count == 0) return false; + uintptr_t p = (uintptr_t)ptr; + + int lo = 0, hi = g_rope_mark_count - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + gc_rope_mark_t *m = &g_rope_marks[mid]; + if (p < m->base) hi = mid - 1; + else if (p >= m->end) lo = mid + 1; + else { + m->has_live = true; + return true; + } + } + + return false; +} + +static void unlink_rope_block(ant_pool_t *pool, ant_pool_block_t *block) { + if (block->prev) block->prev->next = block->next; + else pool->head = block->next; + if (block->next) block->next->prev = block->prev; +} + +void gc_ropes_sweep(ant_t *js) { + for (int i = 0; i < g_rope_mark_count; i++) { + gc_rope_mark_t *m = &g_rope_marks[i]; + if (!m->has_live) { + unlink_rope_block(&js->pool.rope, m->block); + m->block->used = 0; + m->block->next = NULL; + pool_free_set_next(m->block, js->pool.rope.free_head); + pool_block_madvise_free(m->block); + js->pool.rope.free_head = m->block; + }} + + ant_pool_block_t *f = js->pool.rope.free_head; + int kept = 0; + + while (f && kept < 2) { + f = pool_free_next(f); + kept++; + } + + while (f) { + ant_pool_block_t *next = pool_free_next(f); + pool_block_free(f); + f = next; + } + + if (kept > 0) { + f = js->pool.rope.free_head; + for (int k = 1; k < kept && f; k++) f = pool_free_next(f); + if (f) pool_free_set_next(f, NULL); + } else js->pool.rope.free_head = NULL; + + g_rope_mark_count = 0; +} diff --git a/src/gc/strings.c b/src/gc/strings.c new file mode 100644 index 0000000..456d51e --- /dev/null +++ b/src/gc/strings.c @@ -0,0 +1,178 @@ +#include "internal.h" +#include "gc/strings.h" +#include "pool.h" + +#include +#include + +typedef struct { + uint8_t *bits; + size_t n_slots; +} gc_bitmap_t; + +static gc_bitmap_t bitmap_alloc(size_t n_slots) { + size_t nbytes = (n_slots + 7) / 8; + uint8_t *bits = calloc(1, nbytes); + return (gc_bitmap_t){ .bits = bits, .n_slots = n_slots }; +} + +static inline void bitmap_set(gc_bitmap_t *bm, size_t idx) { + if (idx < bm->n_slots && bm->bits) bm->bits[idx / 8] |= (1u << (idx % 8)); +} + +static inline bool bitmap_get(const gc_bitmap_t *bm, size_t idx) { + if (idx >= bm->n_slots || !bm->bits) return false; + return (bm->bits[idx / 8] >> (idx % 8)) & 1u; +} + +static inline void bitmap_free(gc_bitmap_t *bm) { + free(bm->bits); + bm->bits = NULL; + bm->n_slots = 0; +} + +typedef struct { + uintptr_t base; + uintptr_t end; + ant_pool_block_t *block; + ant_pool_bucket_t *bucket; + size_t stride; + gc_bitmap_t bitmap; +} gc_block_mark_t; + +static gc_block_mark_t *g_string_marks = NULL; +static int g_string_mark_count = 0; +static int g_string_mark_cap = 0; + +static void collect_bucket_blocks(ant_pool_bucket_t *bucket) { + if (!bucket || bucket->slot_stride == 0) return; + size_t stride = bucket->slot_stride; + + for (ant_pool_block_t *b = bucket->head; b; b = b->next) { + size_t n_slots = b->used / stride; + if (n_slots == 0) continue; + + if (g_string_mark_count >= g_string_mark_cap) { + int cap = g_string_mark_cap ? g_string_mark_cap * 2 : 32; + g_string_marks = realloc(g_string_marks, (size_t)cap * sizeof(gc_block_mark_t)); + g_string_mark_cap = cap; + } + + gc_block_mark_t *m = &g_string_marks[g_string_mark_count++]; + m->base = (uintptr_t)b->data; + m->end = m->base + b->used; + m->block = b; + m->bucket = bucket; + m->stride = stride; + m->bitmap = bitmap_alloc(n_slots); + } +} + +static int mark_cmp(const void *a, const void *b) { + const gc_block_mark_t *ma = (const gc_block_mark_t *)a; + const gc_block_mark_t *mb = (const gc_block_mark_t *)b; + if (ma->base < mb->base) return -1; + if (ma->base > mb->base) return 1; + return 0; +} + +void gc_strings_begin(ant_t *js) { + g_string_mark_count = 0; + ant_class_pool_t *pool = &js->pool.string; + + for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { + pool->classes[i].slot_free = NULL; + collect_bucket_blocks(&pool->classes[i]); + } + + if (g_string_mark_count > 1) + qsort(g_string_marks, (size_t)g_string_mark_count, sizeof(gc_block_mark_t), mark_cmp); +} + + +void gc_strings_mark(ant_t *js, const void *ptr) { + if (!ptr || g_string_mark_count == 0) return; + uintptr_t p = (uintptr_t)ptr; + + int lo = 0, hi = g_string_mark_count - 1; + while (lo <= hi) { + int mid = lo + (hi - lo) / 2; + gc_block_mark_t *m = &g_string_marks[mid]; + if (p < m->base) hi = mid - 1; + else if (p >= m->end) lo = mid + 1; + else { + size_t offset = p - m->base; + if (offset % m->stride == 0) bitmap_set(&m->bitmap, offset / m->stride); + return; + } + } +} + + +static void unlink_block(ant_pool_bucket_t *bucket, ant_pool_block_t *block) { + if (block->prev) block->prev->next = block->next; + else bucket->head = block->next; + if (block->next) block->next->prev = block->prev; +} + +void gc_strings_sweep(ant_t *js) { + for (int i = 0; i < g_string_mark_count; i++) { + gc_block_mark_t *m = &g_string_marks[i]; + ant_pool_bucket_t *bucket = m->bucket; + + bool any_live = false; + size_t n_slots = m->bitmap.n_slots; + for (size_t j = 0; j < n_slots; j++) { + if (bitmap_get(&m->bitmap, j)) { any_live = true; break; } + } + + if (!any_live && bucket) { + unlink_block(bucket, m->block); + m->block->used = 0; + m->block->next = NULL; + if (bucket->current == m->block) bucket->current = NULL; + pool_free_set_next(m->block, bucket->free_head); + pool_block_madvise_free(m->block); + bucket->free_head = m->block; + } else if (any_live && bucket && m->stride >= sizeof(void *)) { + uintptr_t base = m->base; + for (size_t j = 0; j < n_slots; j++) { + if (!bitmap_get(&m->bitmap, j)) { + void *slot = (void *)(base + j * m->stride); + void *old_head = bucket->slot_free; + memcpy(slot, &old_head, sizeof(void *)); + bucket->slot_free = slot; + }} + } + + bitmap_free(&m->bitmap); + } + + ant_class_pool_t *pool = &js->pool.string; + for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { + ant_pool_bucket_t *bucket = &pool->classes[i]; + ant_pool_block_t *f = bucket->free_head; + int kept = 0; + + while (f && kept < 2) { + f = pool_free_next(f); + kept++; + } + + while (f) { + ant_pool_block_t *next = pool_free_next(f); + pool_block_free(f); + f = next; + } + + f = bucket->free_head; + for (int k = 0; k < kept && f; k++) f = pool_free_next(f); + if (kept > 0) { + f = bucket->free_head; + for (int k = 1; k < kept && f; k++) f = pool_free_next(f); + if (f) pool_free_set_next(f, NULL); + } else bucket->free_head = NULL; + } + + g_string_mark_count = 0; +} diff --git a/src/main.c b/src/main.c index 778fa02..c3b5149 100644 --- a/src/main.c +++ b/src/main.c @@ -1,5 +1,4 @@ #include // IWYU pragma: keep -#include #include #include @@ -10,6 +9,7 @@ #include #include "ant.h" +#include "gc.h" #include "repl.h" #include "debug.h" #include "utils.h" @@ -98,6 +98,10 @@ static const subcommand_t subcommands[] = { }; static void ant_debug_apply(const char *key, const char *val) { + if (strcmp(key, "gc") == 0) { + if (strcmp(val, "disable") == 0) gc_disabled = true; + } + if (strcmp(key, "dump/crprintf") == 0) { if (strcmp(val, "bytecode") == 0 || strcmp(val, "all") == 0) crprintf_set_debug(true); if (strcmp(val, "hex") == 0 || strcmp(val, "all") == 0) crprintf_set_debug_hex(true); @@ -110,7 +114,14 @@ static void ant_debug_apply(const char *key, const char *val) { } } -static void parse_ant_debug_flags(void) { +static inline void setup_console_colors(void) { + crprintf_var("version", ANT_VERSION); + crprintf_var("fatal", "FATAL"); + crprintf_var("error", "Error"); + crprintf_var("warn", "Warning"); +} + +static void parse_ant_debug_flags(void) { const char *env = getenv("ANT_DEBUG"); if (!env || !*env) return; @@ -350,6 +361,7 @@ static int execute_module(ant_t *js, const char *filename) { } int main(int argc, char *argv[]) { + setup_console_colors(); parse_ant_debug_flags(); int filtered_argc = 0; int original_argc = argc; @@ -357,11 +369,6 @@ int main(int argc, char *argv[]) { const char *binary_name = strrchr(argv[0], '/'); binary_name = binary_name ? binary_name + 1 : argv[0]; - - crprintf_var("version", ANT_VERSION); - crprintf_var("fatal", "FATAL"); - crprintf_var("error", "Error"); - crprintf_var("warn", "Warning"); if (strcmp(binary_name, "antx") == 0) { char **exec_argv = try_oom(sizeof(char*) * (argc + 2)); diff --git a/src/modules/async_hooks.c b/src/modules/async_hooks.c index 0428df8..12a0c06 100644 --- a/src/modules/async_hooks.c +++ b/src/modules/async_hooks.c @@ -41,10 +41,10 @@ static ant_value_t async_local_storage_run(ant_t *js, ant_value_t *args, int nar return js_mkerr(js, "AsyncLocalStorage.run() requires an AsyncLocalStorage instance"); } - ant_value_t prev = js_get_slot(js, this_obj, SLOT_DATA); - js_set_slot(js, this_obj, SLOT_DATA, args[0]); + ant_value_t prev = js_get_slot(this_obj, SLOT_DATA); + js_set_slot_wb(js, this_obj, SLOT_DATA, args[0]); ant_value_t result = async_hooks_call_with_tail_args(js, args[1], js_mkundef(), args, nargs, 2); - js_set_slot(js, this_obj, SLOT_DATA, prev); + js_set_slot_wb(js, this_obj, SLOT_DATA, prev); return result; } @@ -58,10 +58,10 @@ static ant_value_t async_local_storage_exit(ant_t *js, ant_value_t *args, int na return js_mkerr(js, "AsyncLocalStorage.exit() requires an AsyncLocalStorage instance"); } - ant_value_t prev = js_get_slot(js, this_obj, SLOT_DATA); - js_set_slot(js, this_obj, SLOT_DATA, js_mkundef()); + ant_value_t prev = js_get_slot(this_obj, SLOT_DATA); + js_set_slot_wb(js, this_obj, SLOT_DATA, js_mkundef()); ant_value_t result = async_hooks_call_with_tail_args(js, args[0], js_mkundef(), args, nargs, 1); - js_set_slot(js, this_obj, SLOT_DATA, prev); + js_set_slot_wb(js, this_obj, SLOT_DATA, prev); return result; } @@ -70,7 +70,7 @@ static ant_value_t async_local_storage_enterWith(ant_t *js, ant_value_t *args, i if (!is_object_type(this_obj)) { return js_mkerr(js, "AsyncLocalStorage.enterWith() requires an AsyncLocalStorage instance"); } - js_set_slot(js, this_obj, SLOT_DATA, nargs > 0 ? args[0] : js_mkundef()); + js_set_slot_wb(js, this_obj, SLOT_DATA, nargs > 0 ? args[0] : js_mkundef()); return js_mkundef(); } @@ -78,13 +78,13 @@ static ant_value_t async_local_storage_getStore(ant_t *js, ant_value_t *args, in (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); if (!is_object_type(this_obj)) return js_mkundef(); - return js_get_slot(js, this_obj, SLOT_DATA); + return js_get_slot(this_obj, SLOT_DATA); } static ant_value_t async_local_storage_disable(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); - if (is_object_type(this_obj)) js_set_slot(js, this_obj, SLOT_DATA, js_mkundef()); + if (is_object_type(this_obj)) js_set_slot_wb(js, this_obj, SLOT_DATA, js_mkundef()); return js_mkundef(); } diff --git a/src/modules/atomics.c b/src/modules/atomics.c index d05c8c7..3b38084 100644 --- a/src/modules/atomics.c +++ b/src/modules/atomics.c @@ -83,7 +83,7 @@ int wait_queue_notify(WaitQueue *queue, int32_t *address, int count) { } static bool get_atomic_array_data(ant_t *js, ant_value_t this_val, TypedArrayData **out_data, uint8_t **out_ptr) { - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer) return false; diff --git a/src/modules/bigint.c b/src/modules/bigint.c index 1f2460e..c07507a 100644 --- a/src/modules/bigint.c +++ b/src/modules/bigint.c @@ -72,8 +72,8 @@ static void bigint_normalize_limbs(uint32_t *limbs, size_t *count) { } static inline const bigint_payload_t *bigint_payload(ant_t *js, ant_value_t v) { - ant_offset_t ofs = (ant_offset_t)vdata(v); - return (const bigint_payload_t *)&js->mem[ofs + sizeof(ant_offset_t)]; + (void)js; + return (const bigint_payload_t *)(uintptr_t)vdata(v); } static inline bool limbs_is_zero(const uint32_t *limbs, size_t count) { @@ -113,16 +113,11 @@ static ant_value_t js_mkbigint_limbs(ant_t *js, const uint32_t *limbs, size_t co return js_mkerr(js, "oom"); } - size_t total_size; - if (!checked_add_size(sizeof(ant_offset_t), payload_size, &total_size)) return js_mkerr(js, "oom"); + bigint_payload_t *payload = (bigint_payload_t *)js_type_alloc( + js, ANT_ALLOC_BIGINT, payload_size, _Alignof(bigint_payload_t) + ); + if (!payload) return js_mkerr(js, "oom"); - ant_offset_t ofs = js_alloc(js, total_size); - if (ofs == (ant_offset_t)~0) return js_mkerr(js, "oom"); - - ant_offset_t header = (ant_offset_t)(payload_size << 4); - memcpy(&js->mem[ofs], &header, sizeof(header)); - - bigint_payload_t *payload = (bigint_payload_t *)&js->mem[ofs + sizeof(ant_offset_t)]; payload->sign = negative ? 1 : 0; payload->pad[0] = 0; payload->pad[1] = 0; @@ -130,7 +125,7 @@ static ant_value_t js_mkbigint_limbs(ant_t *js, const uint32_t *limbs, size_t co payload->limb_count = (uint32_t)count; memcpy(payload->limbs, limbs, limbs_bytes); - return mkval(T_BIGINT, ofs); + return mkval(T_BIGINT, (uintptr_t)payload); } static ant_value_t bigint_from_u64(ant_t *js, uint64_t value) { @@ -1306,7 +1301,7 @@ static ant_value_t builtin_BigInt(ant_t *js, ant_value_t *args, int nargs) { if (vtype(arg) == T_STR) { ant_offset_t slen; ant_offset_t off = vstr(js, arg, &slen); - const char *str = (const char *)&js->mem[off]; + const char *str = (const char *)(uintptr_t)(off); size_t start = 0; size_t end = slen; @@ -1461,16 +1456,16 @@ void init_bigint_module(void) { ant_value_t glob = js_glob(js); ant_value_t object_proto = js->object; - ant_value_t function_proto = js_get_slot(js, glob, SLOT_FUNC_PROTO); + ant_value_t function_proto = js_get_slot(glob, SLOT_FUNC_PROTO); if (vtype(function_proto) == T_UNDEF) function_proto = js_get_ctor_proto(js, "Function", 8); ant_value_t bigint_proto = js_mkobj(js); - js_set_proto(js, bigint_proto, object_proto); + js_set_proto_init(bigint_proto, object_proto); js_setprop(js, bigint_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_bigint_toString)); ant_value_t bigint_ctor_obj = mkobj(js, 0); - js_set_proto(js, bigint_ctor_obj, function_proto); - js_set_slot(js, bigint_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_BigInt)); + js_set_proto_init(bigint_ctor_obj, function_proto); + js_set_slot(bigint_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_BigInt)); js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asIntN", 6), js_mkfun(builtin_BigInt_asIntN)); js_setprop(js, bigint_ctor_obj, js_mkstr(js, "asUintN", 7), js_mkfun(builtin_BigInt_asUintN)); js_setprop_nonconfigurable(js, bigint_ctor_obj, "prototype", 9, bigint_proto); diff --git a/src/modules/buffer.c b/src/modules/buffer.c index f28d6a5..9484bf3 100644 --- a/src/modules/buffer.c +++ b/src/modules/buffer.c @@ -12,7 +12,6 @@ #include "ant.h" #include "errors.h" -#include "arena.h" #include "base64.h" #include "internal.h" #include "silver/engine.h" @@ -204,7 +203,7 @@ static ant_value_t create_typed_array_like( if (is_err(out)) return out; ant_value_t proto = js_get_proto(js, this_val); - if (is_special_object(proto)) js_set_proto(js, out, proto); + if (is_special_object(proto)) js_set_proto_init(out, proto); return out; } @@ -225,18 +224,18 @@ static ant_value_t js_arraybuffer_constructor(ant_t *js, ant_value_t *args, int ant_value_t obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "ArrayBuffer", 11); - - if (is_special_object(proto)) js_set_proto(js, obj, proto); - js_set_slot(js, obj, SLOT_BUFFER, ANT_PTR(data)); + + if (is_special_object(proto)) js_set_proto_init(obj, proto); + js_set_slot(obj, SLOT_BUFFER, ANT_PTR(data)); js_set(js, obj, "byteLength", js_mknum((double)length)); - + return obj; } // ArrayBuffer.prototype.slice(begin, end) static ant_value_t js_arraybuffer_slice(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t data_val = js_get_slot(this_val, SLOT_BUFFER); if (vtype(data_val) != T_NUM) { return js_mkerr(js, "Not an ArrayBuffer"); @@ -260,12 +259,12 @@ static ant_value_t js_arraybuffer_slice(ant_t *js, ant_value_t *args, int nargs) if (!new_data) return js_mkerr(js, "Failed to allocate new ArrayBuffer"); memcpy(new_data->data, data->data + begin, new_length); - + ant_value_t new_obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "ArrayBuffer", 11); - - if (is_special_object(proto)) js_set_proto(js, new_obj, proto); - js_set_slot(js, new_obj, SLOT_BUFFER, ANT_PTR(new_data)); + + if (is_special_object(proto)) js_set_proto_init(new_obj, proto); + js_set_slot(new_obj, SLOT_BUFFER, ANT_PTR(new_data)); js_set(js, new_obj, "byteLength", js_mknum((double)new_length)); return new_obj; @@ -274,7 +273,7 @@ static ant_value_t js_arraybuffer_slice(ant_t *js, ant_value_t *args, int nargs) // ArrayBuffer.prototype.transfer(newLength) static ant_value_t js_arraybuffer_transfer(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t data_val = js_get_slot(this_val, SLOT_BUFFER); if (vtype(data_val) != T_NUM) { return js_mkerr(js, "Not an ArrayBuffer"); @@ -305,12 +304,12 @@ static ant_value_t js_arraybuffer_transfer(ant_t *js, ant_value_t *args, int nar data->is_detached = 1; data->length = 0; js_set(js, this_val, "byteLength", js_mknum(0)); - + ant_value_t new_obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "ArrayBuffer", 11); - - if (is_special_object(proto)) js_set_proto(js, new_obj, proto); - js_set_slot(js, new_obj, SLOT_BUFFER, ANT_PTR(new_data)); + + if (is_special_object(proto)) js_set_proto_init(new_obj, proto); + js_set_slot(new_obj, SLOT_BUFFER, ANT_PTR(new_data)); js_set(js, new_obj, "byteLength", js_mknum((double)new_length)); return new_obj; @@ -324,7 +323,7 @@ static ant_value_t js_arraybuffer_transferToFixedLength(ant_t *js, ant_value_t * // ArrayBuffer.prototype.detached getter static ant_value_t js_arraybuffer_detached_getter(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t data_val = js_get_slot(this_val, SLOT_BUFFER); if (vtype(data_val) != T_NUM) return js_false; ArrayBufferData *data = (ArrayBufferData *)(uintptr_t)js_getnum(data_val); @@ -343,7 +342,7 @@ static ant_value_t typedarray_index_getter(ant_t *js, ant_value_t obj, const cha index = index * 10 + (c - '0'); } - ant_value_t ta_val = js_get_slot(js, obj, SLOT_BUFFER); + ant_value_t ta_val = js_get_slot(obj, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); if (!ta_data || index >= ta_data->length) return js_mkundef(); if (!ta_data->buffer || ta_data->buffer->is_detached) return js_mkundef(); @@ -381,7 +380,7 @@ static bool typedarray_index_setter(ant_t *js, ant_value_t obj, const char *key, index = index * 10 + (c - '0'); } - ant_value_t ta_val = js_get_slot(js, obj, SLOT_BUFFER); + ant_value_t ta_val = js_get_slot(obj, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_val); if (!ta_data || index >= ta_data->length) return true; if (!ta_data->buffer || ta_data->buffer->is_detached) return true; @@ -458,9 +457,9 @@ static bool typedarray_write_number(TypedArrayData *ta_data, size_t index, doubl ant_value_t create_arraybuffer_obj(ant_t *js, ArrayBufferData *buffer) { ant_value_t ab_obj = js_mkobj(js); ant_value_t ab_proto = js_get_ctor_proto(js, "ArrayBuffer", 11); - if (is_special_object(ab_proto)) js_set_proto(js, ab_obj, ab_proto); + if (is_special_object(ab_proto)) js_set_proto_init(ab_obj, ab_proto); - js_set_slot(js, ab_obj, SLOT_BUFFER, js_mknum((double)(uintptr_t)buffer)); + js_set_slot(ab_obj, SLOT_BUFFER, js_mknum((double)(uintptr_t)buffer)); js_set(js, ab_obj, "byteLength", js_mknum((double)buffer->length)); buffer->ref_count++; @@ -484,17 +483,17 @@ ant_value_t create_typed_array_with_buffer( ant_value_t obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, type_name, strlen(type_name)); - if (is_special_object(proto)) js_set_proto(js, obj, proto); + if (is_special_object(proto)) js_set_proto_init(obj, proto); - js_set_slot(js, obj, SLOT_BUFFER, js_mktypedarray(ta_data)); + js_set_slot(obj, SLOT_BUFFER, js_mktypedarray(ta_data)); js_set(js, obj, "length", js_mknum((double)length)); js_set(js, obj, "byteLength", js_mknum((double)(length * element_size))); js_set(js, obj, "byteOffset", js_mknum((double)byte_offset)); js_set(js, obj, "BYTES_PER_ELEMENT", js_mknum((double)element_size)); js_set(js, obj, "buffer", arraybuffer_obj); - js_set_getter(js, obj, typedarray_index_getter); - js_set_setter(js, obj, typedarray_index_setter); + js_set_getter(obj, typedarray_index_getter); + js_set_setter(obj, typedarray_index_setter); return obj; } @@ -541,7 +540,7 @@ static ant_value_t js_typedarray_constructor(ant_t *js, ant_value_t *args, int n return create_typed_array(js, type, buffer, 0, length, type_name); } - ant_value_t buffer_data_val = js_get_slot(js, args[0], SLOT_BUFFER); + ant_value_t buffer_data_val = js_get_slot(args[0], SLOT_BUFFER); if (vtype(buffer_data_val) == T_NUM) { ArrayBufferData *buffer = (ArrayBufferData *)(uintptr_t)js_getnum(buffer_data_val); size_t byte_offset = 0; @@ -633,7 +632,7 @@ static ant_value_t js_typedarray_constructor(ant_t *js, ant_value_t *args, int n // TypedArray.prototype.at(index) static ant_value_t js_typedarray_at(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -667,7 +666,7 @@ static ant_value_t js_typedarray_at(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_typedarray_slice(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -704,7 +703,7 @@ static ant_value_t js_typedarray_slice(ant_t *js, ant_value_t *args, int nargs) // TypedArray.prototype.subarray(begin, end) static ant_value_t js_typedarray_subarray(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -732,7 +731,7 @@ static ant_value_t js_typedarray_subarray(ant_t *js, ant_value_t *args, int narg // TypedArray.prototype.fill(value, start, end) static ant_value_t js_typedarray_fill(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -793,7 +792,7 @@ static ant_value_t js_typedarray_set(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "set requires source argument"); ant_value_t this_val = js_getthis(js); - ant_value_t dst_ta_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t dst_ta_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *dst = (TypedArrayData *)js_gettypedarray(dst_ta_val); if (!dst) return js_mkerr(js, "Invalid TypedArray"); if (!dst->buffer || dst->buffer->is_detached) return js_mkerr(js, "Cannot operate on a detached TypedArray"); @@ -805,7 +804,7 @@ static ant_value_t js_typedarray_set(ant_t *js, ant_value_t *args, int nargs) { if (offset > dst->length) return js_mkerr(js, "Offset out of bounds"); ant_value_t src_val = args[0]; - ant_value_t src_ta_val = js_get_slot(js, src_val, SLOT_BUFFER); + ant_value_t src_ta_val = js_get_slot(src_val, SLOT_BUFFER); TypedArrayData *src_ta = (TypedArrayData *)js_gettypedarray(src_ta_val); if (src_ta && src_ta->buffer && !src_ta->buffer->is_detached) { @@ -847,7 +846,8 @@ static ant_value_t js_typedarray_set(ant_t *js, ant_value_t *args, int nargs) { if (vtype(src_val) == T_STR) { ant_offset_t slen = 0; ant_offset_t soff = vstr(js, src_val, &slen); - if (i < (size_t)slen) value = (unsigned char)js->mem[soff + (ant_offset_t)i]; + const unsigned char *sptr = (const unsigned char *)(uintptr_t)soff; + if (i < (size_t)slen) value = sptr[i]; } else { char idx[24]; size_t idx_len = uint_to_str(idx, sizeof(idx), (uint64_t)i); @@ -864,7 +864,7 @@ static ant_value_t js_typedarray_set(ant_t *js, ant_value_t *args, int nargs) { // TypedArray.prototype.copyWithin(target, start, end) static ant_value_t js_typedarray_copyWithin(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta) return js_mkerr(js, "Invalid TypedArray"); if (!ta->buffer || ta->buffer->is_detached) return js_mkerr(js, "Cannot operate on a detached TypedArray"); @@ -898,7 +898,7 @@ static ant_value_t js_typedarray_copyWithin(ant_t *js, ant_value_t *args, int na static ant_value_t js_typedarray_toReversed(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -926,7 +926,7 @@ static ant_value_t js_typedarray_toReversed(ant_t *js, ant_value_t *args, int na // TypedArray.prototype.toSorted(comparefn) static ant_value_t js_typedarray_toSorted(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -942,7 +942,7 @@ static ant_value_t js_typedarray_toSorted(ant_t *js, ant_value_t *args, int narg free_array_buffer_data(new_buffer); if (is_err(result)) return result; - ant_value_t result_ta_val = js_get_slot(js, result, SLOT_BUFFER); + ant_value_t result_ta_val = js_get_slot(result, SLOT_BUFFER); TypedArrayData *result_ta = (TypedArrayData *)js_gettypedarray(result_ta_val); uint8_t *data = result_ta->buffer->data; @@ -1012,7 +1012,7 @@ static ant_value_t js_typedarray_with(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "with requires index and value"); ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid TypedArray"); @@ -1086,7 +1086,7 @@ static ant_value_t js_dataview_constructor(ant_t *js, ant_value_t *args, int nar return js_mkerr(js, "DataView requires an ArrayBuffer"); } - ant_value_t buffer_data_val = js_get_slot(js, args[0], SLOT_BUFFER); + ant_value_t buffer_data_val = js_get_slot(args[0], SLOT_BUFFER); if (vtype(buffer_data_val) != T_NUM) { return js_mkerr(js, "First argument must be an ArrayBuffer"); } @@ -1120,9 +1120,9 @@ static ant_value_t js_dataview_constructor(ant_t *js, ant_value_t *args, int nar ant_value_t obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "DataView", 8); - if (is_special_object(proto)) js_set_proto(js, obj, proto); + if (is_special_object(proto)) js_set_proto_init(obj, proto); - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(dv_data)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(dv_data)); js_mkprop_fast(js, obj, "buffer", 6, args[0]); js_set_descriptor(js, obj, "buffer", 6, 0); @@ -1137,7 +1137,7 @@ static ant_value_t js_dataview_getUint8(ant_t *js, ant_value_t *args, int nargs) if (nargs < 1) return js_mkerr(js, "getUint8 requires byteOffset"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1159,7 +1159,7 @@ static ant_value_t js_dataview_setUint8(ant_t *js, ant_value_t *args, int nargs) if (nargs < 2) return js_mkerr(js, "setUint8 requires byteOffset and value"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1182,7 +1182,7 @@ static ant_value_t js_dataview_getInt16(ant_t *js, ant_value_t *args, int nargs) if (nargs < 1) return js_mkerr(js, "getInt16 requires byteOffset"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1213,7 +1213,7 @@ static ant_value_t js_dataview_getInt32(ant_t *js, ant_value_t *args, int nargs) if (nargs < 1) return js_mkerr(js, "getInt32 requires byteOffset"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1244,7 +1244,7 @@ static ant_value_t js_dataview_getFloat32(ant_t *js, ant_value_t *args, int narg if (nargs < 1) return js_mkerr(js, "getFloat32 requires byteOffset"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1277,7 +1277,7 @@ static ant_value_t js_dataview_setInt16(ant_t *js, ant_value_t *args, int nargs) if (nargs < 2) return js_mkerr(js, "setInt16 requires byteOffset and value"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1310,7 +1310,7 @@ static ant_value_t js_dataview_setInt32(ant_t *js, ant_value_t *args, int nargs) if (nargs < 2) return js_mkerr(js, "setInt32 requires byteOffset and value"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1347,7 +1347,7 @@ static ant_value_t js_dataview_setFloat32(ant_t *js, ant_value_t *args, int narg if (nargs < 2) return js_mkerr(js, "setFloat32 requires byteOffset and value"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1386,7 +1386,7 @@ static ant_value_t js_dataview_getFloat64(ant_t *js, ant_value_t *args, int narg if (nargs < 1) return js_mkerr(js, "getFloat64 requires byteOffset"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1421,7 +1421,7 @@ static ant_value_t js_dataview_setFloat64(ant_t *js, ant_value_t *args, int narg if (nargs < 2) return js_mkerr(js, "setFloat64 requires byteOffset and value"); ant_value_t this_val = js_getthis(js); - ant_value_t dv_data_val = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t dv_data_val = js_get_slot(this_val, SLOT_DATA); if (vtype(dv_data_val) != T_NUM) { return js_mkerr(js, "Not a DataView"); @@ -1614,7 +1614,7 @@ static ant_value_t js_buffer_allocUnsafe(ant_t *js, ant_value_t *args, int nargs } static ant_value_t typedarray_join_with(ant_t *js, ant_value_t this_val, const char *sep, size_t sep_len) { - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkstr(js, "", 0); @@ -1693,7 +1693,7 @@ static ant_value_t js_buffer_slice(ant_t *js, ant_value_t *args, int nargs) { // Buffer.prototype.toString(encoding) static ant_value_t js_buffer_toString(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid Buffer"); @@ -1755,7 +1755,7 @@ static ant_value_t js_buffer_write(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "write requires a string"); ant_value_t this_val = js_getthis(js); - ant_value_t ta_data_val = js_get_slot(js, this_val, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(this_val, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Invalid Buffer"); @@ -1879,7 +1879,7 @@ static ant_value_t js_buffer_concat(ant_t *js, ant_value_t *args, int nargs) { snprintf(idx, sizeof(idx), "%zu", i); ant_value_t buf = js_get(js, list, idx); - ant_value_t ta_data_val = js_get_slot(js, buf, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(buf, SLOT_BUFFER); TypedArrayData *ta = js_gettypedarray(ta_data_val); if (!ta || !ta->buffer) continue; @@ -1899,8 +1899,8 @@ static ant_value_t js_buffer_concat(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_buffer_compare(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkerr(js, "Buffer.compare requires two arguments"); - ant_value_t ta1_val = js_get_slot(js, args[0], SLOT_BUFFER); - ant_value_t ta2_val = js_get_slot(js, args[1], SLOT_BUFFER); + ant_value_t ta1_val = js_get_slot(args[0], SLOT_BUFFER); + ant_value_t ta2_val = js_get_slot(args[1], SLOT_BUFFER); TypedArrayData *ta1 = js_gettypedarray(ta1_val); TypedArrayData *ta2 = js_gettypedarray(ta2_val); @@ -1940,9 +1940,9 @@ static ant_value_t js_sharedarraybuffer_constructor(ant_t *js, ant_value_t *args ant_value_t obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "SharedArrayBuffer", 17); - - if (is_special_object(proto)) js_set_proto(js, obj, proto); - js_set_slot(js, obj, SLOT_BUFFER, ANT_PTR(data)); + + if (is_special_object(proto)) js_set_proto_init(obj, proto); + js_set_slot(obj, SLOT_BUFFER, ANT_PTR(data)); js_set(js, obj, "byteLength", js_mknum((double)length)); return obj; @@ -1960,7 +1960,7 @@ void init_buffer_module() { ant_value_t arraybuffer_ctor_obj = js_mkobj(js); ant_value_t arraybuffer_proto = js_mkobj(js); - js_set_proto(js, arraybuffer_proto, object_proto); + js_set_proto_init(arraybuffer_proto, object_proto); js_set(js, arraybuffer_proto, "slice", js_mkfun(js_arraybuffer_slice)); js_set(js, arraybuffer_proto, "transfer", js_mkfun(js_arraybuffer_transfer)); @@ -1968,7 +1968,7 @@ void init_buffer_module() { js_set_getter_desc(js, arraybuffer_proto, "detached", 8, js_mkfun(js_arraybuffer_detached_getter), JS_DESC_E); js_set_sym(js, arraybuffer_proto, get_toStringTag_sym(), js_mkstr(js, "ArrayBuffer", 11)); - js_set_slot(js, arraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_arraybuffer_constructor)); + js_set_slot(arraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_arraybuffer_constructor)); js_mkprop_fast(js, arraybuffer_ctor_obj, "prototype", 9, arraybuffer_proto); js_mkprop_fast(js, arraybuffer_ctor_obj, "name", 4, ANT_STRING("ArrayBuffer")); js_set_descriptor(js, arraybuffer_ctor_obj, "name", 4, 0); @@ -1976,7 +1976,7 @@ void init_buffer_module() { js_set(js, glob, "ArrayBuffer", js_obj_to_func(arraybuffer_ctor_obj)); ant_value_t typedarray_proto = js_mkobj(js); - js_set_proto(js, typedarray_proto, object_proto); + js_set_proto_init(typedarray_proto, object_proto); js_set(js, typedarray_proto, "at", js_mkfun(js_typedarray_at)); js_set(js, typedarray_proto, "set", js_mkfun(js_typedarray_set)); @@ -2000,8 +2000,8 @@ void init_buffer_module() { do { \ ant_value_t name##_ctor_obj = js_mkobj(js); \ ant_value_t name##_proto = js_mkobj(js); \ - js_set_proto(js, name##_proto, typedarray_proto); \ - js_set_slot(js, name##_ctor_obj, SLOT_CFUNC, js_mkfun(js_##name##_constructor)); \ + js_set_proto_init(name##_proto, typedarray_proto); \ + js_set_slot(name##_ctor_obj, SLOT_CFUNC, js_mkfun(js_##name##_constructor)); \ js_setprop(js, name##_ctor_obj, js_mkstr(js, "prototype", 9), name##_proto); \ js_mkprop_fast(js, name##_ctor_obj, "name", 4, ANT_STRING(#name)); \ js_set_descriptor(js, name##_ctor_obj, "name", 4, 0); \ @@ -2023,7 +2023,7 @@ void init_buffer_module() { ant_value_t dataview_ctor_obj = js_mkobj(js); ant_value_t dataview_proto = js_mkobj(js); - js_set_proto(js, dataview_proto, object_proto); + js_set_proto_init(dataview_proto, object_proto); js_set(js, dataview_proto, "getUint8", js_mkfun(js_dataview_getUint8)); js_set(js, dataview_proto, "setUint8", js_mkfun(js_dataview_setUint8)); @@ -2037,7 +2037,7 @@ void init_buffer_module() { js_set(js, dataview_proto, "setFloat64", js_mkfun(js_dataview_setFloat64)); js_set_sym(js, dataview_proto, get_toStringTag_sym(), js_mkstr(js, "DataView", 8)); - js_set_slot(js, dataview_ctor_obj, SLOT_CFUNC, js_mkfun(js_dataview_constructor)); + js_set_slot(dataview_ctor_obj, SLOT_CFUNC, js_mkfun(js_dataview_constructor)); js_mkprop_fast(js, dataview_ctor_obj, "prototype", 9, dataview_proto); js_mkprop_fast(js, dataview_ctor_obj, "name", 4, ANT_STRING("DataView")); js_set_descriptor(js, dataview_ctor_obj, "name", 4, 0); @@ -2045,12 +2045,12 @@ void init_buffer_module() { ant_value_t sharedarraybuffer_ctor_obj = js_mkobj(js); ant_value_t sharedarraybuffer_proto = js_mkobj(js); - js_set_proto(js, sharedarraybuffer_proto, object_proto); + js_set_proto_init(sharedarraybuffer_proto, object_proto); js_set(js, sharedarraybuffer_proto, "slice", js_mkfun(js_arraybuffer_slice)); js_set_sym(js, sharedarraybuffer_proto, get_toStringTag_sym(), js_mkstr(js, "SharedArrayBuffer", 17)); - js_set_slot(js, sharedarraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_sharedarraybuffer_constructor)); + js_set_slot(sharedarraybuffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_sharedarraybuffer_constructor)); js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "prototype", 9, sharedarraybuffer_proto); js_mkprop_fast(js, sharedarraybuffer_ctor_obj, "name", 4, ANT_STRING("SharedArrayBuffer")); js_set_descriptor(js, sharedarraybuffer_ctor_obj, "name", 4, 0); @@ -2062,8 +2062,8 @@ void init_buffer_module() { ant_value_t uint8array_ctor = js_get(js, glob, "Uint8Array"); ant_value_t uint8array_proto = js_get(js, uint8array_ctor, "prototype"); - if (is_special_object(uint8array_proto)) js_set_proto(js, buffer_proto, uint8array_proto); - else js_set_proto(js, buffer_proto, typedarray_proto); + if (is_special_object(uint8array_proto)) js_set_proto_init(buffer_proto, uint8array_proto); + else js_set_proto_init(buffer_proto, typedarray_proto); js_set(js, buffer_proto, "slice", js_mkfun(js_buffer_slice)); js_set(js, buffer_proto, "toString", js_mkfun(js_buffer_toString)); @@ -2082,7 +2082,7 @@ void init_buffer_module() { js_set(js, buffer_ctor_obj, "concat", js_mkfun(js_buffer_concat)); js_set(js, buffer_ctor_obj, "compare", js_mkfun(js_buffer_compare)); - js_set_slot(js, buffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_buffer_from)); + js_set_slot(buffer_ctor_obj, SLOT_CFUNC, js_mkfun(js_buffer_from)); js_mkprop_fast(js, buffer_ctor_obj, "prototype", 9, buffer_proto); js_mkprop_fast(js, buffer_ctor_obj, "name", 4, ANT_STRING("Buffer")); js_set_descriptor(js, buffer_ctor_obj, "name", 4, 0); diff --git a/src/modules/builtin.c b/src/modules/builtin.c index 5b7d37d..614409c 100644 --- a/src/modules/builtin.c +++ b/src/modules/builtin.c @@ -14,13 +14,15 @@ #endif #include "ant.h" +#include "gc.h" #include "errors.h" #include "runtime.h" #include "internal.h" +#include "descriptors.h" + #include "silver/engine.h" #include "modules/builtin.h" #include "modules/buffer.h" -#include "modules/collections.h" static struct { ant_t *js; @@ -56,6 +58,11 @@ static ant_value_t js_signal(ant_t *js, ant_value_t *args, int nargs) { return js_mkundef(); } +// Ant.raw.stack() +static ant_value_t js_raw_stack(ant_t *js, ant_value_t *args, int nargs) { + return js_capture_raw_stack(js); +} + // Ant.raw.typeof(ant_value_t) static ant_value_t js_raw_typeof(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Ant.raw.typeof() requires 1 argument"); @@ -63,6 +70,71 @@ static ant_value_t js_raw_typeof(ant_t *js, ant_value_t *args, int nargs) { return js_mknum((double)type); } +// Ant.raw.ctorPropFeedback(constructorFn) +static ant_value_t js_raw_ctor_prop_feedback(ant_t *js, ant_value_t *args, int nargs) { + if (nargs < 1) return js_mkerr(js, "Ant.raw.ctorPropFeedback() requires 1 argument"); + if (vtype(args[0]) != T_FUNC) return js_mkerr(js, "constructor must be a function"); + +#ifndef ANT_JIT + (void)js; + return js_mkerr(js, "constructor property feedback requires ANT_JIT"); +#else + ant_value_t ctor = args[0]; + ant_value_t ctor_obj = js_func_obj(ctor); + ant_value_t target_func = js_get_slot(ctor_obj, SLOT_TARGET_FUNC); + if (vtype(target_func) == T_FUNC) ctor = target_func; + + sv_closure_t *closure = js_func_closure(ctor); + if (!closure || !closure->func) return js_mkundef(); + sv_func_t *fn = closure->func; + + ant_value_t out = js_newobj(js); + js_set(js, out, "samples", js_mknum((double)fn->ctor_prop_samples)); + js_set(js, out, "overflowFrom", js_mknum((double)SV_TFB_CTOR_PROP_OVERFLOW_FROM)); + js_set(js, out, "inobjLimit", js_mknum((double)sv_tfb_ctor_inobj_limit(ctor))); + js_set(js, out, "inobjLimitFrozen", js_bool(sv_tfb_ctor_inobj_limit_frozen(ctor))); + js_set(js, out, "slackRemaining", js_mknum((double)sv_tfb_ctor_inobj_slack_remaining(ctor))); + + ant_value_t bins = js_mkarr(js); + for (uint32_t i = 0; i < SV_TFB_CTOR_PROP_BINS; i++) { + js_arr_push(js, bins, js_mknum((double)fn->ctor_prop_hist[i])); + } + js_set(js, out, "bins", bins); + + if (fn->name) js_set(js, out, "name", js_mkstr(js, fn->name, strlen(fn->name))); + if (fn->filename) js_set(js, out, "filename", js_mkstr(js, fn->filename, strlen(fn->filename))); + + return out; +#endif +} + +static ant_value_t js_raw_gc_mark_profile(ant_t *js, ant_value_t *args, int nargs) { + gc_func_mark_profile_t p = gc_func_mark_profile_get(); + ant_value_t out = js_newobj(js); + + js_set(js, out, "enabled", js_bool(p.enabled)); + js_set(js, out, "collections", js_mknum((double)p.collections)); + js_set(js, out, "funcVisits", js_mknum((double)p.func_visits)); + js_set(js, out, "childEdges", js_mknum((double)p.child_edges)); + js_set(js, out, "constSlots", js_mknum((double)p.const_slots)); + js_set(js, out, "timeNs", js_mknum((double)p.time_ns)); + js_set(js, out, "timeMs", js_mknum((double)p.time_ns / 1000000.0)); + + return out; +} + +static ant_value_t js_raw_gc_mark_profile_enable(ant_t *js, ant_value_t *args, int nargs) { + bool enabled = true; + if (nargs > 0) enabled = js_truthy(js, args[0]); + gc_func_mark_profile_enable(enabled); + return js_bool(enabled); +} + +static ant_value_t js_raw_gc_mark_profile_reset(ant_t *js, ant_value_t *args, int nargs) { + gc_func_mark_profile_reset(); + return js_mkundef(); +} + // Ant.sleep(seconds) static ant_value_t js_sleep(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "Ant.sleep() requires 1 argument"); @@ -94,18 +166,100 @@ static ant_value_t js_stats_fn(ant_t *js, ant_value_t *args, int nargs) { (void) args; (void) nargs; ant_value_t result = js_newobj(js); - js_set(js, result, "arenaUsed", js_mknum((double)js_getbrk(js))); - js_set(js, result, "arenaSize", js_mknum((double)js->size)); + ant_pool_stats_t rope_s = js_pool_stats(&js->pool.rope); + ant_pool_stats_t sym_s = js_pool_stats(&js->pool.symbol); + ant_pool_stats_t bigint_s = js_class_pool_stats(&js->pool.bigint); + ant_pool_stats_t string_s = js_class_pool_stats(&js->pool.string); + + ant_value_t pools = js_newobj(js); + ant_value_t rope_obj = js_newobj(js); + js_set(js, rope_obj, "used", js_mknum((double)rope_s.used)); + js_set(js, rope_obj, "capacity", js_mknum((double)rope_s.capacity)); + js_set(js, rope_obj, "blocks", js_mknum((double)rope_s.blocks)); + js_set(js, pools, "rope", rope_obj); + + ant_value_t sym_obj = js_newobj(js); + js_set(js, sym_obj, "used", js_mknum((double)sym_s.used)); + js_set(js, sym_obj, "capacity", js_mknum((double)sym_s.capacity)); + js_set(js, sym_obj, "blocks", js_mknum((double)sym_s.blocks)); + js_set(js, pools, "symbol", sym_obj); + + ant_value_t bigint_obj = js_newobj(js); + js_set(js, bigint_obj, "used", js_mknum((double)bigint_s.used)); + js_set(js, bigint_obj, "capacity", js_mknum((double)bigint_s.capacity)); + js_set(js, bigint_obj, "blocks", js_mknum((double)bigint_s.blocks)); + js_set(js, pools, "bigint", bigint_obj); + + ant_value_t string_obj = js_newobj(js); + js_set(js, string_obj, "used", js_mknum((double)string_s.used)); + js_set(js, string_obj, "capacity", js_mknum((double)string_s.capacity)); + js_set(js, string_obj, "blocks", js_mknum((double)string_s.blocks)); + js_set(js, pools, "string", string_obj); + + size_t pool_used = rope_s.used + sym_s.used + bigint_s.used + string_s.used; + size_t pool_cap = rope_s.capacity + sym_s.capacity + bigint_s.capacity + string_s.capacity; + js_set(js, pools, "totalUsed", js_mknum((double)pool_used)); + js_set(js, pools, "totalCapacity", js_mknum((double)pool_cap)); + js_set(js, result, "pools", pools); + + size_t obj_count = 0; + size_t obj_bytes = 0; + size_t overflow_bytes = 0; + size_t extra_bytes = 0; + size_t promise_bytes = 0; + size_t proxy_bytes = 0; + size_t exotic_bytes = 0; + size_t array_bytes = 0; + + for (int pass = 0; pass < 3; pass++) { + ant_object_t *head = pass == 0 ? js->objects : pass == 1 ? js->objects_old : js->permanent_objects; + for (ant_object_t *obj = head; obj; obj = obj->next) { + obj_count++; + obj_bytes += sizeof(ant_object_t); + + uint32_t inobj_limit = ant_object_inobj_limit(obj); + if (obj->overflow_prop && obj->prop_count > inobj_limit) + overflow_bytes += (obj->prop_count - inobj_limit) * sizeof(ant_value_t); + if (obj->extra_slots) extra_bytes += obj->extra_count * sizeof(ant_extra_slot_t); + if (obj->promise_state) promise_bytes += sizeof(ant_promise_state_t); + if (obj->proxy_state) proxy_bytes += sizeof(ant_proxy_state_t); + if (obj->exotic_ops) exotic_bytes += sizeof(ant_exotic_ops_t); + if (obj->type_tag == T_ARR && obj->u.array.data) + array_bytes += obj->u.array.cap * sizeof(ant_value_t); + } + } + + ant_value_t alloc = js_newobj(js); + js_set(js, alloc, "objectCount", js_mknum((double)obj_count)); + js_set(js, alloc, "objects", js_mknum((double)obj_bytes)); + js_set(js, alloc, "overflow", js_mknum((double)overflow_bytes)); + js_set(js, alloc, "extraSlots", js_mknum((double)extra_bytes)); + js_set(js, alloc, "promises", js_mknum((double)promise_bytes)); + js_set(js, alloc, "proxies", js_mknum((double)proxy_bytes)); + js_set(js, alloc, "exotic", js_mknum((double)exotic_bytes)); + js_set(js, alloc, "arrays", js_mknum((double)array_bytes)); + size_t shape_bytes = ant_shape_total_bytes(); + js_set(js, alloc, "shapes", js_mknum((double)shape_bytes)); + js_set(js, alloc, "closures", js_mknum((double)js->alloc_bytes.closures)); + js_set(js, alloc, "upvalues", js_mknum((double)js->alloc_bytes.upvalues)); + js_set(js, alloc, "propRefs", js_mknum((double)(js->prop_refs_cap * sizeof(ant_prop_ref_t)))); + + size_t alloc_total = obj_bytes + overflow_bytes + extra_bytes + + promise_bytes + proxy_bytes + exotic_bytes + array_bytes + + shape_bytes + js->alloc_bytes.closures + js->alloc_bytes.upvalues + + js->prop_refs_cap * sizeof(ant_prop_ref_t); + + js_set(js, alloc, "total", js_mknum((double)alloc_total)); + js_set(js, result, "alloc", alloc); + size_t buffer_mem = buffer_get_external_memory(); size_t code_mem = code_arena_get_memory(); - size_t collections_mem = collections_get_external_memory(); - size_t external_total = buffer_mem + code_mem + collections_mem; + size_t external_total = buffer_mem + code_mem; ant_value_t ext = js_newobj(js); js_set(js, ext, "buffers", js_mknum((double)buffer_mem)); js_set(js, ext, "code", js_mknum((double)code_mem)); - js_set(js, ext, "collections", js_mknum((double)collections_mem)); js_set(js, ext, "total", js_mknum((double)external_total)); js_set(js, result, "external", ext); @@ -162,6 +316,11 @@ void init_builtin_module() { js_set(js, ant_obj, "usleep", js_mkfun(js_usleep)); ant_value_t raw_obj = js_newobj(js); + js_set_getter_desc(js, js_as_obj(raw_obj), "stack", 5, js_mkfun(js_raw_stack), JS_DESC_C); js_set(js, raw_obj, "typeof", js_mkfun(js_raw_typeof)); + js_set(js, raw_obj, "ctorPropFeedback", js_mkfun(js_raw_ctor_prop_feedback)); + js_set(js, raw_obj, "gcMarkProfile", js_mkfun(js_raw_gc_mark_profile)); + js_set(js, raw_obj, "gcMarkProfileEnable", js_mkfun(js_raw_gc_mark_profile_enable)); + js_set(js, raw_obj, "gcMarkProfileReset", js_mkfun(js_raw_gc_mark_profile_reset)); js_set(js, ant_obj, "raw", raw_obj); -} \ No newline at end of file +} diff --git a/src/modules/child_process.c b/src/modules/child_process.c index 0e10909..cd25881 100644 --- a/src/modules/child_process.c +++ b/src/modules/child_process.c @@ -28,6 +28,7 @@ #include "internal.h" #include "silver/engine.h" +#include "gc/modules.h" #include "modules/child_process.h" #include "modules/buffer.h" #include "modules/symbol.h" @@ -61,6 +62,12 @@ typedef struct { UT_hash_handle hh; } child_event_t; +typedef enum { + STDIO_PIPE = 0, + STDIO_INHERIT, + STDIO_IGNORE, +} stdio_mode_t; + struct child_process_s { ant_t *js; uv_process_t process; @@ -94,6 +101,7 @@ struct child_process_s { bool keep_alive; int pending_closes; char *cwd; + stdio_mode_t stdio_modes[3]; struct child_process_s *next; struct child_process_s *prev; }; @@ -109,8 +117,9 @@ static void fprint_js_str_raw(FILE *out, ant_t *js, ant_value_t s) { ant_offset_t len = 0; ant_offset_t off = vstr(js, s, &len); - if (off > 0 && len > 0) fwrite(&js->mem[off], 1, (size_t)len, out); - if (len == 0 || js->mem[off + len - 1] != '\n') fputc('\n', out); + const char *ptr = (const char *)(uintptr_t)off; + if (ptr && len > 0) fwrite(ptr, 1, (size_t)len, out); + if (len == 0 || ptr[len - 1] != '\n') fputc('\n', out); } static void log_listener_error(ant_t *js, const char *event_name, ant_value_t err) { @@ -187,11 +196,23 @@ static void emit_stream_event( emit_event(cp, full_name, args, nargs); } -static ant_value_t make_uint8array_chunk(ant_t *js, const char *data, size_t len) { +static ant_value_t make_buffer_chunk(ant_t *js, const char *data, size_t len) { ArrayBufferData *ab = create_array_buffer_data(len); if (!ab) return js_mkerr(js, "Out of memory"); if (len > 0 && data) memcpy(ab->data, data, len); - return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Uint8Array"); + return create_typed_array(js, TYPED_ARRAY_UINT8, ab, 0, len, "Buffer"); +} + +static uv_pipe_t *child_pipe(child_process_t *cp, child_stream_kind_t kind) { +switch (kind) { + case CHILD_STREAM_STDIN: return &cp->stdin_pipe; + case CHILD_STREAM_STDOUT: return &cp->stdout_pipe; + case CHILD_STREAM_STDERR: return &cp->stderr_pipe; + default: return NULL; +}} + +static bool child_stdio_is_pipe(child_process_t *cp, child_stream_kind_t kind) { + return cp->stdio_modes[kind] == STDIO_PIPE; } static void add_pending_child(child_process_t *cp) { @@ -323,18 +344,18 @@ static void on_process_exit(uv_process_t *proc, int64_t exit_status, int term_si cp->pending_closes++; uv_close((uv_handle_t *)proc, on_handle_close); - if (!cp->stdin_closed && !uv_is_closing((uv_handle_t *)&cp->stdin_pipe)) { + if (child_stdio_is_pipe(cp, CHILD_STREAM_STDIN) && !cp->stdin_closed && !uv_is_closing((uv_handle_t *)&cp->stdin_pipe)) { cp->pending_closes++; uv_close((uv_handle_t *)&cp->stdin_pipe, on_handle_close); cp->stdin_closed = true; } - if (!cp->stdout_closed && !uv_is_closing((uv_handle_t *)&cp->stdout_pipe)) { + if (child_stdio_is_pipe(cp, CHILD_STREAM_STDOUT) && !cp->stdout_closed && !uv_is_closing((uv_handle_t *)&cp->stdout_pipe)) { uv_read_stop((uv_stream_t *)&cp->stdout_pipe); cp->pending_closes++; uv_close((uv_handle_t *)&cp->stdout_pipe, on_handle_close); cp->stdout_closed = true; } - if (!cp->stderr_closed && !uv_is_closing((uv_handle_t *)&cp->stderr_pipe)) { + if (child_stdio_is_pipe(cp, CHILD_STREAM_STDERR) && !cp->stderr_closed && !uv_is_closing((uv_handle_t *)&cp->stderr_pipe)) { uv_read_stop((uv_stream_t *)&cp->stderr_pipe); cp->pending_closes++; uv_close((uv_handle_t *)&cp->stderr_pipe, on_handle_close); @@ -372,7 +393,7 @@ static void on_stdout_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *b ant_value_t text_args[1] = { text_data }; emit_event(cp, "stdout", text_args, 1); - ant_value_t byte_data = make_uint8array_chunk(cp->js, buf->base, (size_t)nread); + ant_value_t byte_data = make_buffer_chunk(cp->js, buf->base, (size_t)nread); ant_value_t byte_args[1] = { byte_data }; emit_stream_event(cp, CHILD_STREAM_STDOUT, "data", byte_args, 1); @@ -424,7 +445,7 @@ static void on_stderr_read(uv_stream_t *stream, ssize_t nread, const uv_buf_t *b ant_value_t text_args[1] = { text_data }; emit_event(cp, "stderr", text_args, 1); - ant_value_t byte_data = make_uint8array_chunk(cp->js, buf->base, (size_t)nread); + ant_value_t byte_data = make_buffer_chunk(cp->js, buf->base, (size_t)nread); ant_value_t byte_args[1] = { byte_data }; emit_stream_event(cp, CHILD_STREAM_STDERR, "data", byte_args, 1); @@ -460,7 +481,7 @@ static ant_value_t child_on(ant_t *js, ant_value_t *args, int nargs) { if (vtype(args[0]) != T_STR) return js_mkerr(js, "Event name must be a string"); if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "Callback must be a function"); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(cp_ptr) == T_UNDEF) return js_mkerr(js, "Invalid child process object"); child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); @@ -489,7 +510,7 @@ static ant_value_t child_once(ant_t *js, ant_value_t *args, int nargs) { if (vtype(args[0]) != T_STR) return js_mkerr(js, "Event name must be a string"); if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "Callback must be a function"); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(cp_ptr) == T_UNDEF) return js_mkerr(js, "Invalid child process object"); child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); @@ -515,7 +536,7 @@ static ant_value_t child_once(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_kill(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(cp_ptr) == T_UNDEF) return js_false; child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); @@ -550,7 +571,7 @@ static ant_value_t child_write_impl(ant_t *js, child_process_t *cp, ant_value_t data = js_getstr(js, data_arg, &data_len); if (!data) return js_mkerr(js, "Data must be a string or Buffer"); } else { - ant_value_t ta_data_val = js_get_slot(js, data_arg, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(data_arg, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) { return js_mkerr(js, "Data must be a string or Buffer"); @@ -590,7 +611,7 @@ static ant_value_t child_write(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (nargs < 1) return js_mkerr(js, "write() requires data argument"); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(cp_ptr) == T_UNDEF) return js_mkerr(js, "Invalid child process object"); child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); @@ -599,40 +620,46 @@ static ant_value_t child_write(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_ref(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); + if (vtype(cp_ptr) == T_UNDEF) return this_obj; - child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); + if (!cp) return this_obj; cp->keep_alive = true; if (!uv_is_closing((uv_handle_t *)&cp->process)) uv_ref((uv_handle_t *)&cp->process); - if (!uv_is_closing((uv_handle_t *)&cp->stdin_pipe)) uv_ref((uv_handle_t *)&cp->stdin_pipe); - if (!uv_is_closing((uv_handle_t *)&cp->stdout_pipe)) uv_ref((uv_handle_t *)&cp->stdout_pipe); - if (!uv_is_closing((uv_handle_t *)&cp->stderr_pipe)) uv_ref((uv_handle_t *)&cp->stderr_pipe); + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) { + uv_handle_t *h = (uv_handle_t *)child_pipe(cp, i); + if (child_stdio_is_pipe(cp, i) && !uv_is_closing(h)) uv_ref(h); + } return this_obj; } static ant_value_t child_unref(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); + if (vtype(cp_ptr) == T_UNDEF) return this_obj; - child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); + if (!cp) return this_obj; cp->keep_alive = false; + if (!uv_is_closing((uv_handle_t *)&cp->process)) uv_unref((uv_handle_t *)&cp->process); - if (!uv_is_closing((uv_handle_t *)&cp->stdin_pipe)) uv_unref((uv_handle_t *)&cp->stdin_pipe); - if (!uv_is_closing((uv_handle_t *)&cp->stdout_pipe)) uv_unref((uv_handle_t *)&cp->stdout_pipe); - if (!uv_is_closing((uv_handle_t *)&cp->stderr_pipe)) uv_unref((uv_handle_t *)&cp->stderr_pipe); + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) { + uv_handle_t *h = (uv_handle_t *)child_pipe(cp, i); + if (child_stdio_is_pipe(cp, i) && !uv_is_closing(h)) uv_unref(h); + } + return this_obj; } static ant_value_t child_end(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); - ant_value_t cp_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t cp_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(cp_ptr) == T_UNDEF) return js_mkundef(); child_process_t *cp = (child_process_t *)(uintptr_t)js_getnum(cp_ptr); @@ -652,7 +679,7 @@ static uv_handle_t *child_stream_handle(child_process_t *cp, child_stream_kind_t static ant_value_t child_stream_write(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (nargs < 1) return js_mkerr(js, "write() requires data argument"); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return js_mkerr(js, "Invalid stream object"); child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); if (!ctx || !ctx->cp) return js_mkerr(js, "Invalid stream context"); @@ -662,7 +689,7 @@ static ant_value_t child_stream_write(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_stream_end(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return js_mkundef(); child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); if (!ctx || !ctx->cp) return js_mkundef(); @@ -672,7 +699,7 @@ static ant_value_t child_stream_end(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_stream_ref(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return this_obj; child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); if (!ctx || !ctx->cp) return this_obj; @@ -685,7 +712,7 @@ static ant_value_t child_stream_ref(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_stream_unref(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return this_obj; child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); if (!ctx || !ctx->cp) return this_obj; @@ -698,7 +725,7 @@ static ant_value_t child_stream_unref(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t child_stream_destroy(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t this_obj = js_getthis(js); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return this_obj; child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); if (!ctx || !ctx->cp) return this_obj; @@ -732,7 +759,7 @@ static ant_value_t child_stream_on(ant_t *js, ant_value_t *args, int nargs) { if (vtype(args[0]) != T_STR) return js_mkerr(js, "Event name must be a string"); if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "Callback must be a function"); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return js_mkerr(js, "Invalid stream object"); child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); @@ -769,7 +796,7 @@ static ant_value_t child_stream_once(ant_t *js, ant_value_t *args, int nargs) { if (vtype(args[0]) != T_STR) return js_mkerr(js, "Event name must be a string"); if (vtype(args[1]) != T_FUNC) return js_mkerr(js, "Callback must be a function"); - ant_value_t ctx_ptr = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t ctx_ptr = js_get_slot(this_obj, SLOT_DATA); if (vtype(ctx_ptr) == T_UNDEF) return js_mkerr(js, "Invalid stream object"); child_stream_ctx_t *ctx = (child_stream_ctx_t *)(uintptr_t)js_getnum(ctx_ptr); @@ -812,7 +839,7 @@ static ant_value_t create_child_stream_object(ant_t *js, child_process_t *cp, ch else if (kind == CHILD_STREAM_STDOUT) cp->stdout_ctx = ctx; else cp->stderr_ctx = ctx; - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(ctx)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(ctx)); js_set(js, obj, "on", js_mkfun(child_stream_on)); js_set(js, obj, "once", js_mkfun(child_stream_once)); js_set(js, obj, "ref", js_mkfun(child_stream_ref)); @@ -831,20 +858,26 @@ static ant_value_t create_child_stream_object(ant_t *js, child_process_t *cp, ch static ant_value_t create_child_object(ant_t *js, child_process_t *cp) { ant_value_t obj = js_mkobj(js); - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(cp)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(cp)); js_set(js, obj, "pid", js_mknum((double)cp->process.pid)); js_set(js, obj, "exitCode", js_mknull()); js_set(js, obj, "signalCode", js_mknull()); js_set(js, obj, "killed", js_false); js_set(js, obj, "connected", js_true); - cp->stdin_obj = create_child_stream_object(js, cp, CHILD_STREAM_STDIN); - cp->stdout_obj = create_child_stream_object(js, cp, CHILD_STREAM_STDOUT); - cp->stderr_obj = create_child_stream_object(js, cp, CHILD_STREAM_STDERR); + static const struct { child_stream_kind_t kind; const char *name; } streams[] = { + { CHILD_STREAM_STDIN, "stdin" }, + { CHILD_STREAM_STDOUT, "stdout" }, + { CHILD_STREAM_STDERR, "stderr" }, + }; - js_set(js, obj, "stdin", cp->stdin_obj); - js_set(js, obj, "stdout", cp->stdout_obj); - js_set(js, obj, "stderr", cp->stderr_obj); + ant_value_t *stream_objs[] = { &cp->stdin_obj, &cp->stdout_obj, &cp->stderr_obj }; + for (int i = 0; i < 3; i++) { + if (child_stdio_is_pipe(cp, streams[i].kind)) { + *stream_objs[i] = create_child_stream_object(js, cp, streams[i].kind); + } else *stream_objs[i] = js_mknull(); + js_set(js, obj, streams[i].name, *stream_objs[i]); + } js_set(js, obj, "on", js_mkfun(child_on)); js_set(js, obj, "once", js_mkfun(child_once)); @@ -893,6 +926,25 @@ static void free_args_array(char **args, int count) { free(args); } +static stdio_mode_t parse_stdio_mode(ant_t *js, ant_value_t val) { + if (vtype(val) != T_STR) return STDIO_PIPE; + char *s = js_getstr(js, val, NULL); + if (strcmp(s, "inherit") == 0) return STDIO_INHERIT; + if (strcmp(s, "ignore") == 0) return STDIO_IGNORE; + return STDIO_PIPE; +} + +static void parse_stdio_option(ant_t *js, ant_value_t stdio_val, stdio_mode_t *modes) { +if (vtype(stdio_val) == T_STR) { + stdio_mode_t mode = parse_stdio_mode(js, stdio_val); + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) modes[i] = mode; +} else if (is_special_object(stdio_val)) { + ant_offset_t len = js_arr_len(js, stdio_val); + if (len > 3) len = 3; + for (ant_offset_t i = 0; i < len; i++) + modes[i] = parse_stdio_mode(js, js_arr_get(js, stdio_val, i)); +}} + static ant_value_t builtin_spawn(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkerr(js, "spawn() requires a command"); if (vtype(args[0]) != T_STR) return js_mkerr(js, "Command must be a string"); @@ -914,6 +966,10 @@ static ant_value_t builtin_spawn(ant_t *js, ant_value_t *args, int nargs) { } } + stdio_mode_t stdio_modes[3] = { + STDIO_PIPE, STDIO_PIPE, STDIO_PIPE + }; + if (nargs >= 3 && is_special_object(args[2])) { ant_value_t cwd_val = js_get(js, args[2], "cwd"); if (vtype(cwd_val) == T_STR) { @@ -927,6 +983,9 @@ static ant_value_t builtin_spawn(ant_t *js, ant_value_t *args, int nargs) { ant_value_t detached_val = js_get(js, args[2], "detached"); detached = js_truthy(js, detached_val); + + ant_value_t stdio_val = js_get(js, args[2], "stdio"); + parse_stdio_option(js, stdio_val, stdio_modes); } child_process_t *cp = calloc(1, sizeof(child_process_t)); @@ -943,23 +1002,25 @@ static ant_value_t builtin_spawn(ant_t *js, ant_value_t *args, int nargs) { cp->cwd = cwd; cp->promise = js_mkundef(); cp->keep_alive = true; - - uv_pipe_init(uv_default_loop(), &cp->stdin_pipe, 0); - uv_pipe_init(uv_default_loop(), &cp->stdout_pipe, 0); - uv_pipe_init(uv_default_loop(), &cp->stderr_pipe, 0); - - cp->stdin_pipe.data = cp; - cp->stdout_pipe.data = cp; - cp->stderr_pipe.data = cp; + memcpy(cp->stdio_modes, stdio_modes, sizeof(stdio_modes)); cp->process.data = cp; + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) { + if (stdio_modes[i] == STDIO_PIPE) { + uv_pipe_t *p = child_pipe(cp, i); + uv_pipe_init(uv_default_loop(), p, 0); + p->data = cp; + }} + uv_stdio_container_t stdio[3]; - stdio[0].flags = UV_CREATE_PIPE | UV_READABLE_PIPE; - stdio[0].data.stream = (uv_stream_t *)&cp->stdin_pipe; - stdio[1].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - stdio[1].data.stream = (uv_stream_t *)&cp->stdout_pipe; - stdio[2].flags = UV_CREATE_PIPE | UV_WRITABLE_PIPE; - stdio[2].data.stream = (uv_stream_t *)&cp->stderr_pipe; + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) { + if (stdio_modes[i] == STDIO_INHERIT) { + stdio[i].flags = UV_INHERIT_FD; + stdio[i].data.fd = i; + } else if (stdio_modes[i] == STDIO_IGNORE) stdio[i].flags = UV_IGNORE; else { + stdio[i].flags = UV_CREATE_PIPE | (i == CHILD_STREAM_STDIN ? UV_READABLE_PIPE : UV_WRITABLE_PIPE); + stdio[i].data.stream = (uv_stream_t *)child_pipe(cp, i); + }} char **final_args; int final_argc; @@ -1027,8 +1088,14 @@ static ant_value_t builtin_spawn(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr(js, "Failed to spawn process: %s", uv_strerror(r)); } - uv_read_start((uv_stream_t *)&cp->stdout_pipe, alloc_buffer, on_stdout_read); - uv_read_start((uv_stream_t *)&cp->stderr_pipe, alloc_buffer, on_stderr_read); + static const uv_read_cb read_cbs[] = { NULL, on_stdout_read, on_stderr_read }; + bool *closed[] = { &cp->stdin_closed, &cp->stdout_closed, &cp->stderr_closed }; + + for (int i = CHILD_STREAM_STDIN; i <= CHILD_STREAM_STDERR; i++) { + if (stdio_modes[i] == STDIO_PIPE && read_cbs[i]) { + uv_read_start((uv_stream_t *)child_pipe(cp, i), alloc_buffer, read_cbs[i]); + } else if (stdio_modes[i] != STDIO_PIPE) *closed[i] = true; + } add_pending_child(cp); cp->child_obj = create_child_object(js, cp); @@ -1540,16 +1607,16 @@ int has_pending_child_processes(void) { return 0; } -void child_process_gc_update_roots(GC_OP_VAL_ARGS) { - for (child_process_t *cp = pending_children_head; cp; cp = cp->next) { - op_val(ctx, &cp->child_obj); - op_val(ctx, &cp->stdin_obj); - op_val(ctx, &cp->stdout_obj); - op_val(ctx, &cp->stderr_obj); - op_val(ctx, &cp->promise); - - child_event_t *evt, *tmp; - HASH_ITER(hh, cp->events, evt, tmp) - for (int i = 0; i < evt->count; i++) op_val(ctx, &evt->listeners[i].callback); - } -} +void gc_mark_child_process(ant_t *js, gc_mark_fn mark) { +for (child_process_t *cp = pending_children_head; cp; cp = cp->next) { + mark(js, cp->child_obj); + mark(js, cp->stdin_obj); + mark(js, cp->stdout_obj); + mark(js, cp->stderr_obj); + mark(js, cp->promise); + + child_event_t *evt, *tmp; + HASH_ITER(hh, cp->events, evt, tmp) + for (int i = 0; i < evt->count; i++) mark(js, evt->listeners[i].callback); +}} + diff --git a/src/modules/collections.c b/src/modules/collections.c index b26abf0..0e87672 100644 --- a/src/modules/collections.c +++ b/src/modules/collections.c @@ -3,9 +3,7 @@ #include #include "ant.h" -#include "gc.h" #include "errors.h" -#include "arena.h" #include "runtime.h" #include "internal.h" #include "silver/engine.h" @@ -14,83 +12,51 @@ #include "modules/collections.h" #include "modules/symbol.h" -static map_registry_entry_t *map_registry = NULL; -static size_t map_registry_count = 0; -static size_t map_registry_cap = 0; - -static set_registry_entry_t *set_registry = NULL; -static size_t set_registry_count = 0; -static size_t set_registry_cap = 0; - -static void register_map(map_entry_t **head, ant_offset_t obj_offset) { - if (!head) return; - if (map_registry_count >= map_registry_cap) { - size_t new_cap = map_registry_cap ? map_registry_cap * 2 : 64; - map_registry_entry_t *new_reg = realloc(map_registry, new_cap * sizeof(map_registry_entry_t)); - if (!new_reg) return; - map_registry = new_reg; - map_registry_cap = new_cap; - } - map_registry[map_registry_count].head = head; - map_registry[map_registry_count].obj_offset = obj_offset; - map_registry_count++; -} - -static void register_set(set_entry_t **head, ant_offset_t obj_offset) { - if (!head) return; - if (set_registry_count >= set_registry_cap) { - size_t new_cap = set_registry_cap ? set_registry_cap * 2 : 64; - set_registry_entry_t *new_reg = realloc(set_registry, new_cap * sizeof(set_registry_entry_t)); - if (!new_reg) return; - set_registry = new_reg; - set_registry_cap = new_cap; - } - set_registry[set_registry_count].head = head; - set_registry[set_registry_count].obj_offset = obj_offset; - set_registry_count++; -} - static const char *ant_value_to_key(ant_t *js, ant_value_t val) { if (vtype(val) == T_STR) { ant_offset_t len; ant_offset_t off = vstr(js, val, &len); - return (char *)&js->mem[off]; + return (char *)(uintptr_t)(off); } return js_str(js, val); } map_entry_t **get_map_from_obj(ant_t *js, ant_value_t obj) { - ant_value_t map_val = js_get_slot(js, obj, SLOT_MAP); - if (vtype(map_val) == T_UNDEF) return NULL; - return (map_entry_t **)(uintptr_t)js_getnum(map_val); + (void)js; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || ptr->type_tag != T_MAP) return NULL; + return (map_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value); } set_entry_t **get_set_from_obj(ant_t *js, ant_value_t obj) { - ant_value_t set_val = js_get_slot(js, obj, SLOT_SET); - if (vtype(set_val) == T_UNDEF) return NULL; - return (set_entry_t **)(uintptr_t)js_getnum(set_val); + (void)js; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || ptr->type_tag != T_SET) return NULL; + return (set_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value); } static weakmap_entry_t **get_weakmap_from_obj(ant_t *js, ant_value_t obj) { - ant_value_t wm_val = js_get_slot(js, obj, SLOT_DATA); - if (vtype(wm_val) == T_UNDEF) return NULL; - return (weakmap_entry_t **)(uintptr_t)js_getnum(wm_val); + (void)js; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || ptr->type_tag != T_WEAKMAP) return NULL; + return (weakmap_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value); } static weakset_entry_t **get_weakset_from_obj(ant_t *js, ant_value_t obj) { - ant_value_t ws_val = js_get_slot(js, obj, SLOT_DATA); - if (vtype(ws_val) == T_UNDEF) return NULL; - return (weakset_entry_t **)(uintptr_t)js_getnum(ws_val); + (void)js; + ant_object_t *ptr = js_obj_ptr(obj); + if (!ptr || ptr->type_tag != T_WEAKSET) return NULL; + return (weakset_entry_t **)(uintptr_t)js_getnum(ptr->u.data.value); } static map_iterator_state_t *get_map_iter_state(ant_t *js, ant_value_t obj) { - ant_value_t state_val = js_get_slot(js, obj, SLOT_ITER_STATE); + ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return NULL; return (map_iterator_state_t *)(uintptr_t)js_getnum(state_val); } static set_iterator_state_t *get_set_iter_state(ant_t *js, ant_value_t obj) { - ant_value_t state_val = js_get_slot(js, obj, SLOT_ITER_STATE); + ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return NULL; return (set_iterator_state_t *)(uintptr_t)js_getnum(state_val); } @@ -266,7 +232,7 @@ static ant_value_t create_map_iterator(ant_t *js, ant_value_t map_obj, iter_type state->type = type; ant_value_t iter = js_mkobj(js); - js_set_slot(js, iter, SLOT_ITER_STATE, ANT_PTR(state)); + js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); js_set(js, iter, "next", js_mkfun(map_iter_next)); js_set_sym(js, iter, get_iterator_sym(), js_mkfun(sym_this_cb)); @@ -339,7 +305,7 @@ static ant_value_t create_set_iterator(ant_t *js, ant_value_t set_obj, iter_type state->type = type; ant_value_t iter = js_mkobj(js); - js_set_slot(js, iter, SLOT_ITER_STATE, ANT_PTR(state)); + js_set_slot(iter, SLOT_ITER_STATE, ANT_PTR(state)); js_set(js, iter, "next", js_mkfun(set_iter_next)); js_set_sym(js, iter, get_iterator_sym(), js_mkfun(sym_this_cb)); @@ -611,8 +577,8 @@ static ant_value_t builtin_WeakRef(ant_t *js, ant_value_t *args, int nargs) { ant_value_t wr_obj = js_mkobj(js); ant_value_t wr_proto = js_get_ctor_proto(js, "WeakRef", 7); - if (is_special_object(wr_proto)) js_set_proto(js, wr_obj, wr_proto); - js_set_slot(js, wr_obj, SLOT_DATA, args[0]); + if (is_special_object(wr_proto)) js_set_proto_init(wr_obj, wr_proto); + js_set_slot(wr_obj, SLOT_DATA, args[0]); return wr_obj; } @@ -622,7 +588,7 @@ static ant_value_t weakref_deref(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js->this_val; if (vtype(this_val) != T_OBJ) return js_mkundef(); - ant_value_t target = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t target = js_get_slot(this_val, SLOT_DATA); if (vtype(target) != T_OBJ) return js_mkundef(); return target; @@ -635,10 +601,10 @@ static ant_value_t builtin_FinalizationRegistry(ant_t *js, ant_value_t *args, in ant_value_t fr_obj = js_mkobj(js); ant_value_t fr_proto = js_get_ctor_proto(js, "FinalizationRegistry", 20); - if (is_special_object(fr_proto)) js_set_proto(js, fr_obj, fr_proto); + if (is_special_object(fr_proto)) js_set_proto_init(fr_obj, fr_proto); - js_set_slot(js, fr_obj, SLOT_MAP, mkarr(js)); - js_set_slot(js, fr_obj, SLOT_DATA, args[0]); + js_set_slot(fr_obj, SLOT_MAP, mkarr(js)); + js_set_slot(fr_obj, SLOT_DATA, args[0]); return fr_obj; } @@ -659,7 +625,7 @@ static ant_value_t finreg_register(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr(js, "target and held value must not be the same"); } - ant_value_t registrations = js_get_slot(js, this_val, SLOT_MAP); + ant_value_t registrations = js_get_slot(this_val, SLOT_MAP); if (vtype(registrations) != T_ARR) return js_mkundef(); ant_value_t entry = mkarr(js); @@ -690,7 +656,7 @@ static ant_value_t finreg_unregister(ant_t *js, ant_value_t *args, int nargs) { } ant_value_t token = args[0]; - ant_value_t registrations = js_get_slot(js, this_val, SLOT_MAP); + ant_value_t registrations = js_get_slot(this_val, SLOT_MAP); if (vtype(registrations) != T_ARR) return js_false; ant_offset_t len = js_arr_len(js, registrations); @@ -721,16 +687,15 @@ static ant_value_t map_groupBy(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "callback is not a function"); ant_value_t map_obj = js_mkobj(js); - ant_offset_t obj_offset = (ant_offset_t)vdata(map_obj); + js_obj_ptr(map_obj)->type_tag = T_MAP; ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3); - if (is_special_object(map_proto)) js_set_proto(js, map_obj, map_proto); + if (is_special_object(map_proto)) js_set_proto_init(map_obj, map_proto); map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *)); if (!map_head) return js_mkerr(js, "out of memory"); *map_head = NULL; - register_map(map_head, obj_offset); - js_set_slot(js, map_obj, SLOT_MAP, ANT_PTR(map_head)); + js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head)); ant_offset_t len = js_arr_len(js, items); for (ant_offset_t i = 0; i < len; i++) { @@ -768,22 +733,21 @@ static ant_value_t builtin_Map(ant_t *js, ant_value_t *args, int nargs) { ant_value_t map_obj = js->this_val; if (vtype(map_obj) != T_OBJ) map_obj = js_mkobj(js); if (is_err(map_obj)) return map_obj; - ant_offset_t obj_offset = (ant_offset_t)vdata(map_obj); + js_obj_ptr(map_obj)->type_tag = T_MAP; ant_value_t map_proto = js_get_ctor_proto(js, "Map", 3); ant_value_t instance_proto = js_instance_proto_from_new_target(js, map_proto); - if (is_special_object(instance_proto)) js_set_proto(js, map_obj, instance_proto); + if (is_special_object(instance_proto)) js_set_proto_init(map_obj, instance_proto); map_entry_t **map_head = ant_calloc(sizeof(map_entry_t *)); if (!map_head) return js_mkerr(js, "out of memory"); *map_head = NULL; - register_map(map_head, obj_offset); if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - js_set_slot(js, map_obj, SLOT_CTOR, js->new_target); + js_set_slot(map_obj, SLOT_CTOR, js->new_target); } - js_set_slot(js, map_obj, SLOT_MAP, ANT_PTR(map_head)); + js_set_slot(map_obj, SLOT_DATA, ANT_PTR(map_head)); if (nargs == 0 || vtype(args[0]) != T_ARR) return map_obj; @@ -826,22 +790,21 @@ static ant_value_t builtin_Set(ant_t *js, ant_value_t *args, int nargs) { ant_value_t set_obj = js->this_val; if (vtype(set_obj) != T_OBJ) set_obj = js_mkobj(js); if (is_err(set_obj)) return set_obj; - ant_offset_t obj_offset = (ant_offset_t)vdata(set_obj); + js_obj_ptr(set_obj)->type_tag = T_SET; ant_value_t set_proto = js_get_ctor_proto(js, "Set", 3); ant_value_t instance_proto = js_instance_proto_from_new_target(js, set_proto); - - if (is_special_object(instance_proto)) js_set_proto(js, set_obj, instance_proto); + + if (is_special_object(instance_proto)) js_set_proto_init(set_obj, instance_proto); set_entry_t **set_head = ant_calloc(sizeof(set_entry_t *)); if (!set_head) return js_mkerr(js, "out of memory"); *set_head = NULL; - register_set(set_head, obj_offset); if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - js_set_slot(js, set_obj, SLOT_CTOR, js->new_target); + js_set_slot(set_obj, SLOT_CTOR, js->new_target); } - js_set_slot(js, set_obj, SLOT_SET, ANT_PTR(set_head)); + js_set_slot(set_obj, SLOT_DATA, ANT_PTR(set_head)); if (nargs == 0 || vtype(args[0]) != T_ARR) return set_obj; @@ -874,20 +837,21 @@ static ant_value_t builtin_WeakMap(ant_t *js, ant_value_t *args, int nargs) { ant_value_t wm_obj = js->this_val; if (vtype(wm_obj) != T_OBJ) wm_obj = js_mkobj(js); if (is_err(wm_obj)) return wm_obj; + js_obj_ptr(wm_obj)->type_tag = T_WEAKMAP; ant_value_t wm_proto = js_get_ctor_proto(js, "WeakMap", 7); ant_value_t instance_proto = js_instance_proto_from_new_target(js, wm_proto); - - if (is_special_object(instance_proto)) js_set_proto(js, wm_obj, instance_proto); + + if (is_special_object(instance_proto)) js_set_proto_init(wm_obj, instance_proto); weakmap_entry_t **wm_head = ant_calloc(sizeof(weakmap_entry_t *)); if (!wm_head) return js_mkerr(js, "out of memory"); *wm_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - js_set_slot(js, wm_obj, SLOT_CTOR, js->new_target); + js_set_slot(wm_obj, SLOT_CTOR, js->new_target); } - js_set_slot(js, wm_obj, SLOT_DATA, ANT_PTR(wm_head)); + js_set_slot(wm_obj, SLOT_DATA, ANT_PTR(wm_head)); if (nargs == 0 || vtype(args[0]) != T_ARR) return wm_obj; @@ -931,20 +895,21 @@ static ant_value_t builtin_WeakSet(ant_t *js, ant_value_t *args, int nargs) { ant_value_t ws_obj = js->this_val; if (vtype(ws_obj) != T_OBJ) ws_obj = js_mkobj(js); if (is_err(ws_obj)) return ws_obj; + js_obj_ptr(ws_obj)->type_tag = T_WEAKSET; ant_value_t ws_proto = js_get_ctor_proto(js, "WeakSet", 7); ant_value_t instance_proto = js_instance_proto_from_new_target(js, ws_proto); - - if (is_special_object(instance_proto)) js_set_proto(js, ws_obj, instance_proto); + + if (is_special_object(instance_proto)) js_set_proto_init(ws_obj, instance_proto); weakset_entry_t **ws_head = ant_calloc(sizeof(weakset_entry_t *)); if (!ws_head) return js_mkerr(js, "out of memory"); *ws_head = NULL; if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - js_set_slot(js, ws_obj, SLOT_CTOR, js->new_target); + js_set_slot(ws_obj, SLOT_CTOR, js->new_target); } - js_set_slot(js, ws_obj, SLOT_DATA, ANT_PTR(ws_head)); + js_set_slot(ws_obj, SLOT_DATA, ANT_PTR(ws_head)); if (nargs == 0 || vtype(args[0]) != T_ARR) return ws_obj; @@ -979,7 +944,7 @@ void init_collections_module(void) { ant_value_t tag_sym = get_toStringTag_sym(); ant_value_t map_proto = js_mkobj(js); - js_set_proto(js, map_proto, object_proto); + js_set_proto_init(map_proto, object_proto); js_set(js, map_proto, "set", js_mkfun(map_set)); js_set(js, map_proto, "get", js_mkfun(map_get)); js_set(js, map_proto, "has", js_mkfun(map_has)); @@ -994,7 +959,7 @@ void init_collections_module(void) { js_set_sym(js, map_proto, tag_sym, js_mkstr(js, "Map", 3)); ant_value_t map_ctor = js_mkobj(js); - js_set_slot(js, map_ctor, SLOT_CFUNC, js_mkfun(builtin_Map)); + js_set_slot(map_ctor, SLOT_CFUNC, js_mkfun(builtin_Map)); js_mkprop_fast(js, map_ctor, "prototype", 9, map_proto); js_mkprop_fast(js, map_ctor, "name", 4, ANT_STRING("Map")); js_set_descriptor(js, map_ctor, "name", 4, 0); @@ -1003,7 +968,7 @@ void init_collections_module(void) { js_set(js, glob, "Map", js_obj_to_func(map_ctor)); ant_value_t set_proto = js_mkobj(js); - js_set_proto(js, set_proto, object_proto); + js_set_proto_init(set_proto, object_proto); js_set(js, set_proto, "add", js_mkfun(set_add)); js_set(js, set_proto, "has", js_mkfun(set_has)); js_set(js, set_proto, "delete", js_mkfun(set_delete)); @@ -1017,7 +982,7 @@ void init_collections_module(void) { js_set_sym(js, set_proto, tag_sym, js_mkstr(js, "Set", 3)); ant_value_t set_ctor = js_mkobj(js); - js_set_slot(js, set_ctor, SLOT_CFUNC, js_mkfun(builtin_Set)); + js_set_slot(set_ctor, SLOT_CFUNC, js_mkfun(builtin_Set)); js_mkprop_fast(js, set_ctor, "prototype", 9, set_proto); js_mkprop_fast(js, set_ctor, "name", 4, ANT_STRING("Set")); js_set_descriptor(js, set_ctor, "name", 4, 0); @@ -1025,7 +990,7 @@ void init_collections_module(void) { js_set(js, glob, "Set", js_obj_to_func(set_ctor)); ant_value_t weakmap_proto = js_mkobj(js); - js_set_proto(js, weakmap_proto, object_proto); + js_set_proto_init(weakmap_proto, object_proto); js_set(js, weakmap_proto, "set", js_mkfun(weakmap_set)); js_set(js, weakmap_proto, "get", js_mkfun(weakmap_get)); js_set(js, weakmap_proto, "has", js_mkfun(weakmap_has)); @@ -1033,191 +998,48 @@ void init_collections_module(void) { js_set_sym(js, weakmap_proto, tag_sym, js_mkstr(js, "WeakMap", 7)); ant_value_t weakmap_ctor = js_mkobj(js); - js_set_slot(js, weakmap_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakMap)); + js_set_slot(weakmap_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakMap)); js_mkprop_fast(js, weakmap_ctor, "prototype", 9, weakmap_proto); js_mkprop_fast(js, weakmap_ctor, "name", 4, ANT_STRING("WeakMap")); js_set_descriptor(js, weakmap_ctor, "name", 4, 0); js_set(js, glob, "WeakMap", js_obj_to_func(weakmap_ctor)); ant_value_t weakset_proto = js_mkobj(js); - js_set_proto(js, weakset_proto, object_proto); + js_set_proto_init(weakset_proto, object_proto); js_set(js, weakset_proto, "add", js_mkfun(weakset_add)); js_set(js, weakset_proto, "has", js_mkfun(weakset_has)); js_set(js, weakset_proto, "delete", js_mkfun(weakset_delete)); js_set_sym(js, weakset_proto, tag_sym, js_mkstr(js, "WeakSet", 7)); ant_value_t weakset_ctor = js_mkobj(js); - js_set_slot(js, weakset_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakSet)); + js_set_slot(weakset_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakSet)); js_mkprop_fast(js, weakset_ctor, "prototype", 9, weakset_proto); js_mkprop_fast(js, weakset_ctor, "name", 4, ANT_STRING("WeakSet")); js_set_descriptor(js, weakset_ctor, "name", 4, 0); js_set(js, glob, "WeakSet", js_obj_to_func(weakset_ctor)); ant_value_t weakref_proto = js_mkobj(js); - js_set_proto(js, weakref_proto, object_proto); + js_set_proto_init(weakref_proto, object_proto); js_set(js, weakref_proto, "deref", js_mkfun(weakref_deref)); js_set_sym(js, weakref_proto, tag_sym, js_mkstr(js, "WeakRef", 7)); ant_value_t weakref_ctor = js_mkobj(js); - js_set_slot(js, weakref_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakRef)); + js_set_slot(weakref_ctor, SLOT_CFUNC, js_mkfun(builtin_WeakRef)); js_mkprop_fast(js, weakref_ctor, "prototype", 9, weakref_proto); js_mkprop_fast(js, weakref_ctor, "name", 4, ANT_STRING("WeakRef")); js_set_descriptor(js, weakref_ctor, "name", 4, 0); js_set(js, glob, "WeakRef", js_obj_to_func(weakref_ctor)); ant_value_t finreg_proto = js_mkobj(js); - js_set_proto(js, finreg_proto, object_proto); + js_set_proto_init(finreg_proto, object_proto); js_set(js, finreg_proto, "register", js_mkfun(finreg_register)); js_set(js, finreg_proto, "unregister", js_mkfun(finreg_unregister)); js_set_sym(js, finreg_proto, tag_sym, js_mkstr(js, "FinalizationRegistry", 20)); ant_value_t finreg_ctor = js_mkobj(js); - js_set_slot(js, finreg_ctor, SLOT_CFUNC, js_mkfun(builtin_FinalizationRegistry)); + js_set_slot(finreg_ctor, SLOT_CFUNC, js_mkfun(builtin_FinalizationRegistry)); js_mkprop_fast(js, finreg_ctor, "prototype", 9, finreg_proto); js_mkprop_fast(js, finreg_ctor, "name", 4, ANT_STRING("FinalizationRegistry")); js_set_descriptor(js, finreg_ctor, "name", 4, 0); js_set(js, glob, "FinalizationRegistry", js_obj_to_func(finreg_ctor)); } - -void collections_gc_reserve_roots(void (*op_val)(void *, ant_value_t *), void *ctx) { - for (size_t i = 0; i < map_registry_count; i++) { - ant_value_t map_obj = mkval(T_OBJ, map_registry[i].obj_offset); - op_val(ctx, &map_obj); map_entry_t **head = map_registry[i].head; - if (head && *head) { - map_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) op_val(ctx, &entry->value); - } - } - - for (size_t i = 0; i < set_registry_count; i++) { - ant_value_t set_obj = mkval(T_OBJ, set_registry[i].obj_offset); - op_val(ctx, &set_obj); set_entry_t **head = set_registry[i].head; - if (head && *head) { - set_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) op_val(ctx, &entry->value); - } - } -} - -static void free_map_entries(map_entry_t **head) { - if (!head || !*head) return; - map_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) { - HASH_DEL(*head, entry); - free(entry->key); - free(entry); - } -} - -static void free_set_entries(set_entry_t **head) { - if (!head || !*head) return; - set_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) { - HASH_DEL(*head, entry); - free(entry->key); - free(entry); - } -} - -void collections_gc_update_roots(ant_offset_t (*weak_off)(void *ctx, ant_offset_t old), GC_OP_VAL_ARGS) { - size_t write_idx = 0; - - for (size_t i = 0; i < map_registry_count; i++) { - ant_offset_t old_off = map_registry[i].obj_offset; - ant_offset_t new_off = weak_off(ctx, old_off); - - if (new_off == (ant_offset_t)~0) { - free_map_entries(map_registry[i].head); - free(map_registry[i].head); - continue; - } - - map_registry[i].obj_offset = new_off; - - map_entry_t **head = map_registry[i].head; - if (head && *head) { - map_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) op_val(ctx, &entry->value); - } - - if (write_idx != i) map_registry[write_idx] = map_registry[i]; - write_idx++; - } - map_registry_count = write_idx; - - write_idx = 0; - for (size_t i = 0; i < set_registry_count; i++) { - ant_offset_t old_off = set_registry[i].obj_offset; - ant_offset_t new_off = weak_off(ctx, old_off); - - if (new_off == (ant_offset_t)~0) { - free_set_entries(set_registry[i].head); - free(set_registry[i].head); - continue; - } - - set_registry[i].obj_offset = new_off; - - set_entry_t **head = set_registry[i].head; - if (head && *head) { - set_entry_t *entry, *tmp; - HASH_ITER(hh, *head, entry, tmp) op_val(ctx, &entry->value); - } - - if (write_idx != i) set_registry[write_idx] = set_registry[i]; - write_idx++; - } - set_registry_count = write_idx; -} - -void cleanup_collections_module(void) { - for (size_t i = 0; i < map_registry_count; i++) { - free_map_entries(map_registry[i].head); - free(map_registry[i].head); - } - free(map_registry); - map_registry = NULL; - map_registry_count = 0; - map_registry_cap = 0; - - for (size_t i = 0; i < set_registry_count; i++) { - free_set_entries(set_registry[i].head); - free(set_registry[i].head); - } - free(set_registry); - set_registry = NULL; - set_registry_count = 0; - set_registry_cap = 0; -} - -size_t collections_get_external_memory(void) { - size_t total = 0; - - total += map_registry_cap * sizeof(map_registry_entry_t); - for (size_t i = 0; i < map_registry_count; i++) { - total += sizeof(map_entry_t *); - if (map_registry[i].head) { - map_entry_t *entry, *tmp; - HASH_ITER(hh, *map_registry[i].head, entry, tmp) { - total += sizeof(map_entry_t); - if (entry->key) total += strlen(entry->key) + 1; - } - } - } - - total += set_registry_cap * sizeof(set_registry_entry_t); - for (size_t i = 0; i < set_registry_count; i++) { - total += sizeof(set_entry_t *); - if (set_registry[i].head) { - set_entry_t *entry, *tmp; - HASH_ITER(hh, *set_registry[i].head, entry, tmp) { - total += sizeof(set_entry_t); - if (entry->key) total += strlen(entry->key) + 1; - } - } - } - - return total; -} - -#undef CLEANUP_REGISTRY diff --git a/src/modules/crypto.c b/src/modules/crypto.c index 59648a0..5371d6f 100644 --- a/src/modules/crypto.c +++ b/src/modules/crypto.c @@ -151,7 +151,7 @@ static ant_value_t js_crypto_get_random_values(ant_t *js, ant_value_t *args, int return js_mkerr(js, "libsodium initialization failed"); } - ant_value_t ta_data_val = js_get_slot(js, args[0], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[0], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer) { return js_mkerr(js, "argument must be a TypedArray"); diff --git a/src/modules/date.c b/src/modules/date.c index e6ffe83..f1089ed 100644 --- a/src/modules/date.c +++ b/src/modules/date.c @@ -96,7 +96,7 @@ static bool date_this_time_value(ant_t *js, ant_value_t this_val, double *out, a return false; } - ant_value_t t = js_get_slot(js, js_as_obj(this_val), SLOT_DATA); + ant_value_t t = js_get_slot(js_as_obj(this_val), SLOT_DATA); *out = (vtype(t) == T_NUM) ? tod(t) : JS_NAN; return true; } @@ -106,7 +106,7 @@ static ant_value_t date_set_this_time_value(ant_t *js, ant_value_t this_val, dou return js_mkerr_typed(js, JS_ERR_TYPE, "not a Date object"); } - js_set_slot(js, js_as_obj(this_val), SLOT_DATA, tov(v)); + js_set_slot(js_as_obj(this_val), SLOT_DATA, tov(v)); return tov(v); } @@ -758,7 +758,7 @@ static bool date_parse_string_to_ms(ant_t *js, ant_value_t arg, double *out_ms) return true; } - memcpy(buf, &js->mem[off], (size_t)len); + memcpy(buf, (const void *)(uintptr_t)off, (size_t)len); buf[len] = '\0'; int fields[9]; @@ -809,8 +809,8 @@ static ant_value_t builtin_Date(ant_t *js, ant_value_t *args, int nargs) { val = (double)date_now(); ant_value_t tmp = js_mkobj(js); ant_value_t date_proto = js_get_ctor_proto(js, "Date", 4); - if (is_object_type(date_proto)) js_set_proto(js, tmp, date_proto); - js_set_slot(js, tmp, SLOT_DATA, tov(val)); + if (is_object_type(date_proto)) js_set_proto_init(tmp, date_proto); + js_set_slot(tmp, SLOT_DATA, tov(val)); return get_date_string(js, tmp, kDateToStringSpec); } @@ -820,7 +820,7 @@ static ant_value_t builtin_Date(ant_t *js, ant_value_t *args, int nargs) { ant_value_t input = args[0]; if (is_object_type(input) && is_date_instance(js, input)) { - ant_value_t tv = js_get_slot(js, js_as_obj(input), SLOT_DATA); + ant_value_t tv = js_get_slot(js_as_obj(input), SLOT_DATA); val = (vtype(tv) == T_NUM) ? tod(tv) : JS_NAN; val = date_timeclip(val); } else { @@ -851,7 +851,7 @@ static ant_value_t builtin_Date(ant_t *js, ant_value_t *args, int nargs) { val = (i == n) ? date_make_fields(&fields, 1) : JS_NAN; } - js_set_slot(js, js_as_obj(js->this_val), SLOT_DATA, tov(val)); + js_set_slot(js_as_obj(js->this_val), SLOT_DATA, tov(val)); return js->this_val; } @@ -1280,7 +1280,7 @@ void init_date_module(void) { ant_value_t function_proto = js_get_ctor_proto(js, "Function", 8); ant_value_t date_proto = js_mkobj(js); - js_set_proto(js, date_proto, object_proto); + js_set_proto_init(date_proto, object_proto); static const date_method_entry_t kDateProtoMethods[] = { DATE_METHOD_ENTRY("valueOf", builtin_Date_valueOf), @@ -1338,8 +1338,8 @@ void init_date_module(void) { } ant_value_t date_ctor_obj = js_mkobj(js); - js_set_proto(js, date_ctor_obj, function_proto); - js_set_slot(js, date_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Date)); + js_set_proto_init(date_ctor_obj, function_proto); + js_set_slot(date_ctor_obj, SLOT_CFUNC, js_mkfun(builtin_Date)); static const date_method_entry_t kDateCtorMethods[] = { DATE_METHOD_ENTRY("now", builtin_Date_now), DATE_METHOD_ENTRY("parse", builtin_Date_parse), diff --git a/src/modules/events.c b/src/modules/events.c index 37a6e43..f2eedfb 100644 --- a/src/modules/events.c +++ b/src/modules/events.c @@ -6,12 +6,12 @@ #include "ant.h" #include "errors.h" -#include "arena.h" #include "runtime.h" #include "internal.h" #include "descriptors.h" #include "silver/engine.h" +#include "gc/modules.h" #include "modules/events.h" #include "modules/symbol.h" @@ -47,7 +47,7 @@ static inline void remove_listener_at(EventType *evt, int i) { } static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) { - ant_value_t slot = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t slot = js_get_slot(this_obj, SLOT_DATA); if (vtype(slot) == T_UNDEF) { EventType **events = ant_calloc(sizeof(EventType *)); @@ -61,7 +61,7 @@ static EventType **get_or_create_emitter_events(ant_t *js, ant_value_t this_obj) reg->next = emitter_registry; emitter_registry = reg; - js_set_slot(js, this_obj, SLOT_DATA, ANT_PTR(events)); + js_set_slot(this_obj, SLOT_DATA, ANT_PTR(events)); return events; } @@ -588,13 +588,14 @@ void init_events_module() { js_set(js, global, "EventTarget", js_obj_to_func_ex(eventtarget_ctor, SV_CALL_IS_DEFAULT_CTOR)); } -void events_gc_update_roots(void (*op_val)(void *, ant_value_t *), void *ctx) { +void gc_mark_events(ant_t *js, gc_mark_fn mark) { EventType *evt, *tmp; HASH_ITER(hh, global_events, evt, tmp) { - for (int i = 0; i < evt->listener_count; i++) op_val(ctx, &evt->listeners[i].listener); + for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); } for (emitter_reg_t *reg = emitter_registry; reg; reg = reg->next) { - HASH_ITER(hh, *reg->events, evt, tmp) - for (int i = 0; i < evt->listener_count; i++) op_val(ctx, &evt->listeners[i].listener); + HASH_ITER(hh, *reg->events, evt, tmp) + for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); } } + diff --git a/src/modules/fetch.c b/src/modules/fetch.c index d12765f..dfe6e34 100644 --- a/src/modules/fetch.c +++ b/src/modules/fetch.c @@ -16,6 +16,7 @@ #include "modules/fetch.h" #include "modules/json.h" #include "modules/timer.h" +#include "gc/modules.h" typedef struct { char *data; @@ -74,7 +75,7 @@ static ant_value_t fetch_fail_oom(ant_t *js, ant_value_t promise, fetch_request_ static ant_value_t response_text(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); - ant_value_t body = js_get_slot(js, this, SLOT_DATA); + ant_value_t body = js_get_slot(this, SLOT_DATA); ant_value_t promise = js_mkpromise(js); js_resolve_promise(js, promise, body); @@ -84,7 +85,7 @@ static ant_value_t response_text(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t response_json(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); - ant_value_t body = js_get_slot(js, this, SLOT_DATA); + ant_value_t body = js_get_slot(this, SLOT_DATA); ant_value_t parsed = js_json_parse(js, &body, 1); ant_value_t promise = js_mkpromise(js); @@ -102,7 +103,7 @@ static ant_value_t create_response(ant_t *js, int status, const char *body, size js_set(js, response_obj, "ok", js_bool(status >= 200 && status < 300)); js_set(js, response_obj, "status", js_mknum(status)); - js_set_slot(js, response_obj, SLOT_DATA, body_str); + js_set_slot(response_obj, SLOT_DATA, body_str); js_set(js, response_obj, "text", js_mkfun(response_text)); js_set(js, response_obj, "json", js_mkfun(response_json)); @@ -191,7 +192,7 @@ static ant_value_t do_fetch_microtask(ant_t *js, ant_value_t *args, int nargs) { ant_value_t current_func = js_getcurrentfunc(js); ant_value_t url_val = js_get(js, current_func, "url"); ant_value_t options_val = js_get(js, current_func, "options"); - ant_value_t promise = js_get_slot(js, current_func, SLOT_DATA); + ant_value_t promise = js_get_slot(current_func, SLOT_DATA); char *url_str = js_getstr(js, url_val, NULL); if (!url_str) { @@ -325,8 +326,8 @@ static ant_value_t js_fetch(ant_t *js, ant_value_t *args, int nargs) { ant_value_t promise = js_mkpromise(js); ant_value_t wrapper_obj = js_mkobj(js); - js_set_slot(js, wrapper_obj, SLOT_DATA, promise); - js_set_slot(js, wrapper_obj, SLOT_CFUNC, js_mkfun(do_fetch_microtask)); + js_set_slot(wrapper_obj, SLOT_DATA, promise); + js_set_slot(wrapper_obj, SLOT_CFUNC, js_mkfun(do_fetch_microtask)); js_set(js, wrapper_obj, "url", url_val); js_set(js, wrapper_obj, "options", options_val); @@ -345,14 +346,15 @@ int has_pending_fetches(void) { return pending_requests && utarray_len(pending_requests) > 0; } -void fetch_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_fetch(ant_t *js, gc_mark_fn mark) { if (!pending_requests) return; unsigned int len = utarray_len(pending_requests); for (unsigned int i = 0; i < len; i++) { fetch_request_t **reqp = (fetch_request_t **)utarray_eltptr(pending_requests, i); if (reqp && *reqp) { - op_val(ctx, &(*reqp)->promise); - op_val(ctx, &(*reqp)->headers_obj); + mark(js, (*reqp)->promise); + mark(js, (*reqp)->headers_obj); } } } + diff --git a/src/modules/ffi.c b/src/modules/ffi.c index 2eed60c..887caf3 100644 --- a/src/modules/ffi.c +++ b/src/modules/ffi.c @@ -17,6 +17,7 @@ #include #include "errors.h" +#include "gc/modules.h" #include "internal.h" #include "silver/engine.h" @@ -993,11 +994,12 @@ ret_undef: return js_mkundef(); } -void ffi_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_ffi(ant_t *js, gc_mark_fn mark) { pthread_mutex_lock(&ffi_callbacks_mutex); ffi_callback_t *cb, *tmp; HASH_ITER(hh, ffi_callbacks, cb, tmp) { - op_val(ctx, &cb->js_func); + mark(js, cb->js_func); } pthread_mutex_unlock(&ffi_callbacks_mutex); } + diff --git a/src/modules/fs.c b/src/modules/fs.c index a8f6fcc..427a003 100644 --- a/src/modules/fs.c +++ b/src/modules/fs.c @@ -19,6 +19,8 @@ #include "runtime.h" #include "descriptors.h" +#include "gc/modules.h" + #include "modules/fs.h" #include "modules/buffer.h" #include "modules/symbol.h" @@ -405,10 +407,10 @@ static void on_stat_complete(uv_fs_t *uv_req) { ant_value_t stat_obj = js_mkobj(req->js); ant_value_t proto = js_get_ctor_proto(req->js, "Stats", 5); - if (is_object_type(proto)) js_set_proto(req->js, stat_obj, proto); + if (is_object_type(proto)) js_set_proto_init(stat_obj, proto); uv_stat_t *st = &uv_req->statbuf; - js_set_slot(req->js, stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); + js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); js_set(req->js, stat_obj, "size", js_mknum((double)st->st_size)); js_set(req->js, stat_obj, "mode", js_mknum((double)st->st_mode)); js_set(req->js, stat_obj, "uid", js_mknum((double)st->st_uid)); @@ -1066,7 +1068,7 @@ static ant_value_t builtin_fs_rmdir(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); - ant_value_t mode_val = js_get_slot(js, this, SLOT_DATA); + ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); @@ -1076,7 +1078,7 @@ static ant_value_t stat_isFile(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t stat_isDirectory(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); - ant_value_t mode_val = js_get_slot(js, this, SLOT_DATA); + ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); @@ -1086,7 +1088,7 @@ static ant_value_t stat_isDirectory(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t stat_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this = js_getthis(js); - ant_value_t mode_val = js_get_slot(js, this, SLOT_DATA); + ant_value_t mode_val = js_get_slot(this, SLOT_DATA); if (vtype(mode_val) != T_NUM) return js_false; mode_t mode = (mode_t)js_getnum(mode_val); @@ -1097,9 +1099,9 @@ static ant_value_t stat_isSymbolicLink(ant_t *js, ant_value_t *args, int nargs) static ant_value_t create_stats_object(ant_t *js, struct stat *st) { ant_value_t stat_obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "Stats", 5); - if (is_special_object(proto)) js_set_proto(js, stat_obj, proto); + if (is_special_object(proto)) js_set_proto_init(stat_obj, proto); - js_set_slot(js, stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); + js_set_slot(stat_obj, SLOT_DATA, js_mknum((double)st->st_mode)); js_set(js, stat_obj, "size", js_mknum((double)st->st_size)); js_set(js, stat_obj, "mode", js_mknum((double)st->st_mode)); js_set(js, stat_obj, "uid", js_mknum((double)st->st_uid)); @@ -1391,7 +1393,7 @@ static ant_value_t builtin_fs_readSync(ant_t *js, ant_value_t *args, int nargs) int fd = (int)js_getnum(args[0]); - ant_value_t ta_data_val = js_get_slot(js, args[1], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[1], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "readSync() second argument must be a Buffer, TypedArray, or DataView"); @@ -1455,7 +1457,7 @@ static ant_value_t builtin_fs_writeSync(ant_t *js, ant_value_t *args, int nargs) return js_mknum((double)result); } - ant_value_t ta_data_val = js_get_slot(js, args[1], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[1], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "writeSync() second argument must be a Buffer, TypedArray, DataView, or string"); @@ -1515,7 +1517,7 @@ static ant_value_t builtin_fs_write_fd(ant_t *js, ant_value_t *args, int nargs) write_data = str; write_len = str_len; } else { - ant_value_t ta_data_val = js_get_slot(js, args[1], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[1], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data || !ta_data->buffer || !ta_data->buffer->data) return js_mkerr(js, "write() second argument must be a Buffer, TypedArray, DataView, or string"); @@ -1597,7 +1599,7 @@ static ant_value_t builtin_fs_writevSync(ant_t *js, ant_value_t *args, int nargs for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); - ant_value_t ta_val = js_get_slot(js, item, SLOT_BUFFER); + ant_value_t ta_val = js_get_slot(item, SLOT_BUFFER); TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_val); if (!ta || !ta->buffer || !ta->buffer->data) { free(bufs); @@ -1635,7 +1637,7 @@ static ant_value_t builtin_fs_writev_fd(ant_t *js, ant_value_t *args, int nargs) size_t total_len = 0; for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); - ant_value_t ta_val = js_get_slot(js, item, SLOT_BUFFER); + ant_value_t ta_val = js_get_slot(item, SLOT_BUFFER); TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_val); if (!ta || !ta->buffer || !ta->buffer->data) return js_mkerr(js, "writev() buffers must contain ArrayBufferViews"); @@ -1654,7 +1656,7 @@ static ant_value_t builtin_fs_writev_fd(ant_t *js, ant_value_t *args, int nargs) size_t off = 0; for (ant_offset_t i = 0; i < arr_len; i++) { ant_value_t item = js_arr_get(js, args[1], i); - ant_value_t ta_val = js_get_slot(js, item, SLOT_BUFFER); + ant_value_t ta_val = js_get_slot(item, SLOT_BUFFER); TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(ta_val); memcpy(req->data + off, ta->buffer->data + ta->byte_offset, ta->byte_length); off += ta->byte_length; @@ -1891,11 +1893,11 @@ static ant_value_t fs_make_constants(ant_t *js) { static ant_value_t builtin_fs_promises_getter(ant_t *js, ant_value_t *args, int nargs) { ant_value_t getter_fn = js_getcurrentfunc(js); - ant_value_t cached = js_get_slot(js, getter_fn, SLOT_DATA); + ant_value_t cached = js_get_slot(getter_fn, SLOT_DATA); if (is_object_type(cached)) return cached; ant_value_t promises = fs_promises_library(js); - js_set_slot(js, getter_fn, SLOT_DATA, promises); + js_set_slot(getter_fn, SLOT_DATA, promises); return promises; } @@ -1949,11 +1951,12 @@ int has_pending_fs_ops(void) { return pending_requests && utarray_len(pending_requests) > 0; } -void fs_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_fs(ant_t *js, gc_mark_fn mark) { if (!pending_requests) return; unsigned int len = utarray_len(pending_requests); for (unsigned int i = 0; i < len; i++) { fs_request_t **reqp = (fs_request_t **)utarray_eltptr(pending_requests, i); - if (reqp && *reqp) { op_val(ctx, &(*reqp)->promise); } + if (reqp && *reqp) { mark(js, (*reqp)->promise); } } } + diff --git a/src/modules/io.c b/src/modules/io.c index a358850..8bdc927 100644 --- a/src/modules/io.c +++ b/src/modules/io.c @@ -339,6 +339,11 @@ void print_repl_value(ant_t *js, ant_value_t val, FILE *stream) { return; } + if (vtype(val) == T_OBJ && vtype(js_get_slot(val, SLOT_ERR_TYPE)) != T_UNDEF) { + const char *stack = get_str_prop(js, val, "stack", 5, NULL); + if (stack) { io_print(stack, stream); fputc('\n', stream); return; } + } + char cbuf[512]; js_cstr_t cstr = js_to_cstr(js, val, cbuf, sizeof(cbuf)); @@ -501,9 +506,6 @@ static const char *get_slot_name(internal_slot_t slot) { [SLOT_PROTO] = "PROTO", [SLOT_FUNC_PROTO] = "FUNC_PROTO", [SLOT_ASYNC_PROTO] = "ASYNC_PROTO", - [SLOT_FROZEN] = "FROZEN", - [SLOT_SEALED] = "SEALED", - [SLOT_EXTENSIBLE] = "EXTENSIBLE", [SLOT_BUFFER] = "BUFFER", [SLOT_TARGET_FUNC] = "TARGET_FUNC", [SLOT_NAME] = "NAME", @@ -526,7 +528,6 @@ static const char *get_slot_name(internal_slot_t slot) { [SLOT_NO_FUNC_DECLS] = "NO_FUNC_DECLS", [SLOT_ITER_STATE] = "ITER_STATE", [SLOT_ENTRIES] = "ENTRIES", - [SLOT_DENSE_BUF] = "DENSE_BUF", }; if (slot < sizeof(slot_names) / sizeof(slot_names[0]) && slot_names[slot]) { @@ -538,7 +539,6 @@ static const char *get_slot_name(internal_slot_t slot) { static const char *get_type_name(int type) { static const char *type_names[] = { [T_OBJ] = "object", - [T_PROP] = "property", [T_STR] = "string", [T_UNDEF] = "undefined", [T_NULL] = "null", @@ -562,7 +562,7 @@ static const char *get_type_name(int type) { return type_names[type] ? type_names[type] : "unknown"; } -static bool inspect_was_visited(inspect_visited_t *v, ant_offset_t off) { +static bool inspect_was_visited(inspect_visited_t *v, uintptr_t off) { for (int i = 0; i < v->count; i++) if (v->visited[i] == off) return true; return false; } @@ -571,10 +571,10 @@ static void inspect_print_indent(FILE *stream, int depth) { for (int i = 0; i < depth; i++) fprintf(stream, " "); } -static void inspect_mark_visited(inspect_visited_t *v, ant_offset_t off) { +static void inspect_mark_visited(inspect_visited_t *v, uintptr_t off) { if (v->count >= v->capacity) { v->capacity = v->capacity ? v->capacity * 2 : 32; - v->visited = realloc(v->visited, v->capacity * sizeof(ant_offset_t)); + v->visited = realloc(v->visited, v->capacity * sizeof(uintptr_t)); } v->visited[v->count++] = off; } @@ -618,7 +618,7 @@ void inspect_value(ant_t *js, ant_value_t val, FILE *stream, int depth, inspect_ void inspect_object(ant_t *js, ant_value_t obj, FILE *stream, int depth, inspect_visited_t *visited) { int type = vtype(obj); obj = js_as_obj(obj); - ant_offset_t obj_off = (ant_offset_t)vdata(obj); + uintptr_t obj_off = (uintptr_t)vdata(obj); if (inspect_was_visited(visited, obj_off)) { fprintf(stream, "[Circular *%llu]", (u64)obj_off); @@ -634,7 +634,7 @@ void inspect_object(ant_t *js, ant_value_t obj, FILE *stream, int depth, inspect fprintf(stream, "[[Slots]]: {\n"); for (int slot = SLOT_NONE + 1; slot < SLOT_MAX; slot++) { - ant_value_t slot_val = js_get_slot(js, obj, (internal_slot_t)slot); + ant_value_t slot_val = js_get_slot(obj, (internal_slot_t)slot); int t = vtype(slot_val); if (t == T_UNDEF) continue; @@ -652,7 +652,7 @@ void inspect_object(ant_t *js, ant_value_t obj, FILE *stream, int depth, inspect fprintf(stream, "%.0f", js_getnum(slot_val)); break; default: - if ((t == T_OBJ || t == T_FUNC || t == T_PROMISE) && inspect_was_visited(visited, (ant_offset_t)vdata(js_as_obj(slot_val)))) + if ((t == T_OBJ || t == T_FUNC || t == T_PROMISE) && inspect_was_visited(visited, (uintptr_t)vdata(js_as_obj(slot_val)))) fprintf(stream, "[Circular *%llu]", (u64)vdata(js_as_obj(slot_val))); else if (t == T_OBJ || t == T_FUNC || t == T_PROMISE) fprintf(stream, "<%s @%llu>", get_type_name(t), (u64)vdata(js_as_obj(slot_val))); diff --git a/src/modules/json.c b/src/modules/json.c index 5ee05fb..2e27485 100644 --- a/src/modules/json.c +++ b/src/modules/json.c @@ -111,7 +111,7 @@ typedef struct { char *key; size_t key_len; ant_value_t value; } prop_entry; static int should_skip_prop(ant_t *js, const char *key, size_t key_len, ant_value_t value) { if (is_internal_prop(key, (ant_offset_t)key_len)) return 1; if (!is_special_object(value)) return 0; - return vtype(js_get_slot(js, value, SLOT_CODE)) == T_CFUNC; + return vtype(js_get_slot(value, SLOT_CODE)) == T_CFUNC; } static prop_entry *collect_props(ant_t *js, ant_value_t val, int *out_count) { @@ -308,7 +308,7 @@ static ant_value_t apply_reviver(ant_t *js, ant_value_t holder, const char *key, char idxstr[32]; snprintf(idxstr, sizeof(idxstr), "%d", i); ant_value_t new_elem = apply_reviver(js, val, idxstr, reviver); - if (vtype(new_elem) == T_UNDEF) js_del(js, val, idxstr); + if (vtype(new_elem) == T_UNDEF) js_delete_prop(js, val, idxstr, strlen(idxstr)); else js_set(js, val, idxstr, new_elem); } } else { @@ -335,7 +335,7 @@ static ant_value_t apply_reviver(ant_t *js, ant_value_t holder, const char *key, size_t klen; char *kstr = js_getstr(js, key_str, &klen); ant_value_t new_val = apply_reviver(js, val, kstr, reviver); - if (vtype(new_val) == T_UNDEF) js_del(js, val, kstr); + if (vtype(new_val) == T_UNDEF) js_delete_prop(js, val, kstr, strlen(kstr)); else js_set(js, val, kstr, new_val); } } diff --git a/src/modules/lmdb.c b/src/modules/lmdb.c index b84351c..a5cf3e0 100644 --- a/src/modules/lmdb.c +++ b/src/modules/lmdb.c @@ -1,13 +1,13 @@ #include // IWYU pragma: keep #include "ant.h" -#include "arena.h" #include "errors.h" #include "internal.h" #include "modules/lmdb.h" #include "modules/buffer.h" #include "modules/symbol.h" #include "descriptors.h" +#include "gc/modules.h" #include #include @@ -298,7 +298,7 @@ static bool js_to_mdb_val(ant_t *js, ant_value_t input, MDB_val *out) { } if (vtype(input) == T_OBJ) { - ant_value_t slot = js_get_slot(js, input, SLOT_BUFFER); + ant_value_t slot = js_get_slot(input, SLOT_BUFFER); if (vtype(slot) == T_TYPEDARRAY) { TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); @@ -975,7 +975,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { ant_value_t env_ctor_obj = js_mkobj(js); ant_value_t env_proto = js_mkobj(js); - js_set_proto(js, env_proto, object_proto); + js_set_proto_init(env_proto, object_proto); js_set(js, env_proto, "openDB", js_mkfun(lmdb_env_open_db)); js_set(js, env_proto, "beginTxn", js_mkfun(lmdb_env_begin_txn)); js_set(js, env_proto, "close", js_mkfun(lmdb_env_close_method)); @@ -983,7 +983,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { js_set(js, env_proto, "stat", js_mkfun(lmdb_env_stat_method)); js_set(js, env_proto, "info", js_mkfun(lmdb_env_info_method)); js_set_sym(js, env_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBEnv", 7)); - js_set_slot(js, env_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_env_constructor)); + js_set_slot(env_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_env_constructor)); js_mkprop_fast(js, env_ctor_obj, "prototype", 9, env_proto); js_mkprop_fast(js, env_ctor_obj, "name", 4, ANT_STRING("LMDBEnv")); js_set_descriptor(js, env_ctor_obj, "name", 4, 0); @@ -991,7 +991,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { ant_value_t db_ctor_obj = js_mkobj(js); ant_value_t db_proto = js_mkobj(js); - js_set_proto(js, db_proto, object_proto); + js_set_proto_init(db_proto, object_proto); js_set(js, db_proto, "get", js_mkfun(lmdb_db_get)); js_set(js, db_proto, "getBytes", js_mkfun(lmdb_db_get_bytes)); js_set(js, db_proto, "getString", js_mkfun(lmdb_db_get_string)); @@ -1001,7 +1001,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { js_set(js, db_proto, "drop", js_mkfun(lmdb_db_drop)); js_set(js, db_proto, "close", js_mkfun(lmdb_db_close)); js_set_sym(js, db_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBDatabase", 12)); - js_set_slot(js, db_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_db_constructor)); + js_set_slot(db_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_db_constructor)); js_mkprop_fast(js, db_ctor_obj, "prototype", 9, db_proto); js_mkprop_fast(js, db_ctor_obj, "name", 4, ANT_STRING("LMDBDatabase")); js_set_descriptor(js, db_ctor_obj, "name", 4, 0); @@ -1009,7 +1009,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { ant_value_t txn_ctor_obj = js_mkobj(js); ant_value_t txn_proto = js_mkobj(js); - js_set_proto(js, txn_proto, object_proto); + js_set_proto_init(txn_proto, object_proto); js_set(js, txn_proto, "get", js_mkfun(lmdb_txn_get)); js_set(js, txn_proto, "getBytes", js_mkfun(lmdb_txn_get_bytes)); js_set(js, txn_proto, "getString", js_mkfun(lmdb_txn_get_string)); @@ -1018,7 +1018,7 @@ static void ensure_lmdb_prototypes(ant_t *js) { js_set(js, txn_proto, "commit", js_mkfun(lmdb_txn_commit)); js_set(js, txn_proto, "abort", js_mkfun(lmdb_txn_abort)); js_set_sym(js, txn_proto, get_toStringTag_sym(), js_mkstr(js, "LMDBTxn", 7)); - js_set_slot(js, txn_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_txn_constructor)); + js_set_slot(txn_ctor_obj, SLOT_CFUNC, js_mkfun(lmdb_txn_constructor)); js_mkprop_fast(js, txn_ctor_obj, "prototype", 9, txn_proto); js_mkprop_fast(js, txn_ctor_obj, "name", 4, ANT_STRING("LMDBTxn")); js_set_descriptor(js, txn_ctor_obj, "name", 4, 0); @@ -1035,27 +1035,27 @@ static void ensure_lmdb_prototypes(ant_t *js) { static ant_value_t make_env_obj(ant_t *js, lmdb_env_handle_t *env) { ensure_lmdb_prototypes(js); ant_value_t obj = js_mkobj(js); - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(env)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(env)); register_env_ref(obj, env); - if (is_special_object(lmdb_types.env_proto)) js_set_proto(js, obj, lmdb_types.env_proto); + if (is_special_object(lmdb_types.env_proto)) js_set_proto_init(obj, lmdb_types.env_proto); return obj; } static ant_value_t make_db_obj(ant_t *js, lmdb_db_handle_t *db) { ensure_lmdb_prototypes(js); ant_value_t obj = js_mkobj(js); - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(db)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(db)); register_db_ref(obj, db); - if (is_special_object(lmdb_types.db_proto)) js_set_proto(js, obj, lmdb_types.db_proto); + if (is_special_object(lmdb_types.db_proto)) js_set_proto_init(obj, lmdb_types.db_proto); return obj; } static ant_value_t make_txn_obj(ant_t *js, lmdb_txn_handle_t *txn) { ensure_lmdb_prototypes(js); ant_value_t obj = js_mkobj(js); - js_set_slot(js, obj, SLOT_DATA, ANT_PTR(txn)); + js_set_slot(obj, SLOT_DATA, ANT_PTR(txn)); register_txn_ref(obj, txn); - if (is_special_object(lmdb_types.txn_proto)) js_set_proto(js, obj, lmdb_types.txn_proto); + if (is_special_object(lmdb_types.txn_proto)) js_set_proto_init(obj, lmdb_types.txn_proto); return obj; } @@ -1096,18 +1096,18 @@ ant_value_t lmdb_library(ant_t *js) { return lib; } -void lmdb_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_lmdb(ant_t *js, gc_mark_fn mark) { if (lmdb_types.ready) { - op_val(ctx, &lmdb_types.env_ctor); - op_val(ctx, &lmdb_types.db_ctor); - op_val(ctx, &lmdb_types.txn_ctor); - op_val(ctx, &lmdb_types.env_proto); - op_val(ctx, &lmdb_types.db_proto); - op_val(ctx, &lmdb_types.txn_proto); + mark(js, lmdb_types.env_ctor); + mark(js, lmdb_types.db_ctor); + mark(js, lmdb_types.txn_ctor); + mark(js, lmdb_types.env_proto); + mark(js, lmdb_types.db_proto); + mark(js, lmdb_types.txn_proto); } - for (lmdb_env_ref_t *ref = env_refs; ref; ref = ref->next) op_val(ctx, &ref->obj); - for (lmdb_db_ref_t *ref = db_refs; ref; ref = ref->next) op_val(ctx, &ref->obj); - for (lmdb_txn_ref_t *ref = txn_refs; ref; ref = ref->next) op_val(ctx, &ref->obj); + for (lmdb_env_ref_t *ref = env_refs; ref; ref = ref->next) mark(js, ref->obj); + for (lmdb_db_ref_t *ref = db_refs; ref; ref = ref->next) mark(js, ref->obj); + for (lmdb_txn_ref_t *ref = txn_refs; ref; ref = ref->next) mark(js, ref->obj); } void cleanup_lmdb_module(void) { diff --git a/src/modules/localstorage.c b/src/modules/localstorage.c index e4d438b..705c9ae 100644 --- a/src/modules/localstorage.c +++ b/src/modules/localstorage.c @@ -6,7 +6,6 @@ #include "ant.h" #include "errors.h" -#include "arena.h" #include "runtime.h" #include "internal.h" #include "descriptors.h" diff --git a/src/modules/math.c b/src/modules/math.c index ba94243..fbe91ef 100644 --- a/src/modules/math.c +++ b/src/modules/math.c @@ -283,7 +283,7 @@ void init_math_module(void) { ant_value_t object_proto = js->object; ant_value_t math_obj = mkobj(js, 0); - js_set_proto(js, math_obj, object_proto); + js_set_proto_init(math_obj, object_proto); js_setprop(js, math_obj, js_mkstr(js, "E", 1), tov(M_E)); js_setprop(js, math_obj, js_mkstr(js, "LN10", 4), tov(M_LN10)); js_setprop(js, math_obj, js_mkstr(js, "LN2", 3), tov(M_LN2)); diff --git a/src/modules/module.c b/src/modules/module.c index e4b2ff6..5c5a881 100644 --- a/src/modules/module.c +++ b/src/modules/module.c @@ -24,20 +24,20 @@ static ant_value_t builtin_createRequire_call(ant_t *js, ant_value_t *args, int return js_mkerr(js, "require() expects a string specifier"); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t data = js_get_slot(js, fn, SLOT_DATA); + ant_value_t data = js_get_slot(fn, SLOT_DATA); const char *base_path = js_module_eval_active_filename(js); if (vtype(data) == T_STR) { ant_offset_t plen = 0; ant_offset_t poff = vstr(js, data, &plen); - base_path = (const char *)&js->mem[poff]; + base_path = (const char *)(uintptr_t)(poff); } ant_value_t ns = js_esm_import_sync_from(js, args[0], base_path); if (is_err(ns)) return ns; if (vtype(ns) == T_OBJ) { - ant_value_t default_export = js_get_slot(js, ns, SLOT_DEFAULT); + ant_value_t default_export = js_get_slot(ns, SLOT_DEFAULT); if (vtype(default_export) != T_UNDEF) return default_export; } return ns; @@ -49,13 +49,13 @@ static ant_value_t builtin_createRequire_resolve(ant_t *js, ant_value_t *args, i return js_mkerr(js, "require.resolve() expects a string specifier"); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t data = js_get_slot(js, fn, SLOT_DATA); + ant_value_t data = js_get_slot(fn, SLOT_DATA); const char *base_path = js_module_eval_active_filename(js); if (vtype(data) == T_STR) { ant_offset_t dlen = 0; ant_offset_t doff = vstr(js, data, &dlen); - base_path = (const char *)&js->mem[doff]; + base_path = (const char *)(uintptr_t)(doff); } ant_value_t resolved = js_esm_resolve_specifier(js, args[0], base_path); @@ -65,7 +65,7 @@ static ant_value_t builtin_createRequire_resolve(ant_t *js, ant_value_t *args, i ant_offset_t len = 0; ant_offset_t off = vstr(js, resolved, &len); - const char *s = (const char *)&js->mem[off]; + const char *s = (const char *)(uintptr_t)(off); static const char *prefix = "file://"; if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) { @@ -119,7 +119,7 @@ static ant_value_t builtin_resolveFilename(ant_t *js, ant_value_t *args, int nar if (vtype(parent_filename) == T_STR) { ant_offset_t plen = 0; ant_offset_t poff = vstr(js, parent_filename, &plen); - base_path = (const char *)&js->mem[poff]; + base_path = (const char *)(uintptr_t)(poff); } } @@ -130,7 +130,7 @@ static ant_value_t builtin_resolveFilename(ant_t *js, ant_value_t *args, int nar ant_offset_t len = 0; ant_offset_t off = vstr(js, resolved, &len); - const char *s = (const char *)&js->mem[off]; + const char *s = (const char *)(uintptr_t)(off); static const char *prefix = "file://"; if ((size_t)len >= strlen(prefix) && strncmp(s, prefix, strlen(prefix)) == 0) { diff --git a/src/modules/napi.c b/src/modules/napi.c index 8ed4207..a7ec86f 100644 --- a/src/modules/napi.c +++ b/src/modules/napi.c @@ -67,14 +67,14 @@ struct napi_callback_info__ { struct napi_ref__ { ant_napi_env_t *env; - ant_handle_t root; + ant_value_t ref_val; napi_value value; uint32_t refcount; }; struct napi_deferred__ { ant_napi_env_t *env; - ant_handle_t promise_root; + ant_value_t promise_val; bool settled; }; @@ -128,7 +128,7 @@ typedef struct napi_tsfn_item { struct napi_threadsafe_function__ { ant_napi_env_t *env; - ant_handle_t func_root; + ant_value_t func_val; napi_threadsafe_function_call_js call_js_cb; node_api_basic_finalize thread_finalize_cb; void *thread_finalize_data; @@ -254,14 +254,14 @@ static napi_status napi_check_pending_from_result(napi_env env, ant_value_t resu } static bool napi_slot_get_u64(ant_t *js, ant_value_t obj, internal_slot_t slot, uint64_t *out) { - ant_value_t value = js_get_slot(js, obj, slot); + ant_value_t value = js_get_slot(obj, slot); if (vtype(value) != T_NUM) return false; *out = (uint64_t)js_getnum(value); return true; } static void napi_slot_set_u64(ant_t *js, ant_value_t obj, internal_slot_t slot, uint64_t v) { - js_set_slot(js, obj, slot, js_mknum((double)v)); + js_set_slot(obj, slot, js_mknum((double)v)); } static napi_external_entry_t *napi_find_external(ant_t *js, napi_value value) { @@ -287,7 +287,7 @@ static bool napi_get_typedarray_data( napi_value value, TypedArrayData **out ) { - ant_value_t slot = js_get_slot(js, (ant_value_t)value, SLOT_BUFFER); + ant_value_t slot = js_get_slot((ant_value_t)value, SLOT_BUFFER); if (vtype(slot) != T_TYPEDARRAY) return false; TypedArrayData *ta = (TypedArrayData *)js_gettypedarray(slot); if (!ta || !ta->buffer || ta->buffer->is_detached) return false; @@ -359,7 +359,7 @@ static int napi_desc_flags(napi_property_attributes attributes) { static ant_value_t napi_callback_trampoline(ant_t *js, ant_value_t *args, int nargs) { ant_value_t current = js_getcurrentfunc(js); - ant_value_t data_slot = js_get_slot(js, current, SLOT_DATA); + ant_value_t data_slot = js_get_slot(current, SLOT_DATA); if (vtype(data_slot) != T_NUM) return js_mkundef(); napi_callback_binding_t *binding = (napi_callback_binding_t *)(uintptr_t)js_getnum(data_slot); @@ -410,6 +410,7 @@ static napi_status napi_create_function_common( binding->data = data; ant_value_t fn = js_heavy_mkfun(nenv->js, napi_callback_trampoline, ANT_PTR(binding)); + js_mark_constructor(fn, true); if (utf8name && utf8name[0]) { size_t nlen = (length == NAPI_AUTO_LENGTH) ? strlen(utf8name) : length; js_set(nenv->js, fn, "name", js_mkstr(nenv->js, utf8name, nlen)); @@ -448,7 +449,7 @@ static napi_status napi_make_error_object( js_set(js, err, "message", message); ant_value_t proto = js_get_ctor_proto(js, name, strlen(name)); - if (is_object_type(proto)) js_set_proto(js, err, proto); + if (is_object_type(proto)) js_set_proto_init(err, proto); *result = (napi_value)err; return napi_set_last(env, napi_ok, NULL); @@ -495,7 +496,7 @@ static void napi_tsfn_async_cb(uv_async_t *handle) { break; } - ant_value_t cb = js_deref(js, tsfn->func_root); + ant_value_t cb = tsfn->func_val; if (tsfn->call_js_cb) { tsfn->call_js_cb((napi_env)tsfn->env, (napi_value)cb, tsfn->context, item->data); } else if (napi_is_callable(cb)) { @@ -515,7 +516,7 @@ static void napi_tsfn_close_cb(uv_handle_t *handle) { } if (tsfn->env && tsfn->env->js) { - js_root_update(tsfn->env->js, tsfn->func_root, js_mkundef()); + tsfn->func_val = js_mkundef(); } uv_mutex_destroy(&tsfn->mutex); @@ -641,7 +642,7 @@ ant_value_t napi_load_native_module(ant_t *js, const char *module_path, ant_valu if (!is_object_type(ns)) return exports_val; setprop_cstr(js, ns, "default", 7, exports_val); - js_set_slot(js, ns, SLOT_DEFAULT, exports_val); + js_set_slot(ns, SLOT_DEFAULT, exports_val); if (!is_object_type(exports_val)) return exports_val; ant_iter_t iter = js_prop_iter_begin(js, exports_val); @@ -875,7 +876,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_promise( ant_value_t p = js_mkpromise(nenv->js); def->env = nenv; - def->promise_root = js_root(nenv->js, p); + def->promise_val = p; def->settled = false; *deferred = (napi_deferred)def; @@ -894,10 +895,10 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_resolve_deferred( return napi_set_last(env, napi_invalid_arg, "invalid deferred"); } - ant_value_t promise = js_deref(nenv->js, def->promise_root); + ant_value_t promise = def->promise_val; js_resolve_promise(nenv->js, promise, (ant_value_t)resolution); def->settled = true; - js_root_update(nenv->js, def->promise_root, js_mkundef()); + def->promise_val = js_mkundef(); return napi_set_last(env, napi_ok, NULL); } @@ -912,10 +913,10 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_reject_deferred( return napi_set_last(env, napi_invalid_arg, "invalid deferred"); } - ant_value_t promise = js_deref(nenv->js, def->promise_root); + ant_value_t promise = def->promise_val; js_reject_promise(nenv->js, promise, (ant_value_t)rejection); def->settled = true; - js_root_update(nenv->js, def->promise_root, js_mkundef()); + def->promise_val = js_mkundef(); return napi_set_last(env, napi_ok, NULL); } @@ -1018,7 +1019,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_typedarray( return napi_set_last(env, napi_invalid_arg, "invalid argument"); } - ant_value_t slot = js_get_slot(nenv->js, (ant_value_t)arraybuffer, SLOT_BUFFER); + ant_value_t slot = js_get_slot((ant_value_t)arraybuffer, SLOT_BUFFER); if (vtype(slot) != T_NUM) return napi_set_last(env, napi_arraybuffer_expected, "arraybuffer expected"); ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)js_getnum(slot); @@ -1077,8 +1078,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_reference( ref->env = nenv; ref->value = value; ref->refcount = initial_refcount; - ref->root = js_root(nenv->js, (ant_value_t)value); - if (initial_refcount == 0) js_root_update(nenv->js, ref->root, js_mkundef()); + ref->ref_val = (initial_refcount > 0) ? (ant_value_t)value : js_mkundef(); *result = (napi_ref)ref; return napi_set_last(env, napi_ok, NULL); @@ -1091,7 +1091,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_delete_reference( ant_napi_env_t *nenv = (ant_napi_env_t *)env; struct napi_ref__ *r = (struct napi_ref__ *)ref; if (!nenv || !nenv->js || !r) return napi_set_last((napi_env)env, napi_invalid_arg, "invalid argument"); - js_root_update(nenv->js, r->root, js_mkundef()); + r->ref_val = js_mkundef(); free(r); return napi_set_last((napi_env)env, napi_ok, NULL); } @@ -1105,7 +1105,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_reference_ref( struct napi_ref__ *r = (struct napi_ref__ *)ref; if (!nenv || !nenv->js || !r) return napi_set_last(env, napi_invalid_arg, "invalid argument"); - if (r->refcount == 0) js_root_update(nenv->js, r->root, (ant_value_t)r->value); + if (r->refcount == 0) r->ref_val = (ant_value_t)r->value; r->refcount++; if (result) *result = r->refcount; @@ -1123,7 +1123,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_reference_unref( if (r->refcount == 0) return napi_set_last(env, napi_invalid_arg, "reference count already zero"); r->refcount--; - if (r->refcount == 0) js_root_update(nenv->js, r->root, js_mkundef()); + if (r->refcount == 0) r->ref_val = js_mkundef(); if (result) *result = r->refcount; return napi_set_last(env, napi_ok, NULL); @@ -1138,7 +1138,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_reference_value( struct napi_ref__ *r = (struct napi_ref__ *)ref; if (!nenv || !nenv->js || !r || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); - if (r->refcount > 0) *result = (napi_value)js_deref(nenv->js, r->root); + if (r->refcount > 0) *result = (napi_value)r->ref_val; else *result = r->value; return napi_set_last(env, napi_ok, NULL); @@ -1343,7 +1343,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_arraybuffer_info( if (!nenv || !nenv->js) return napi_set_last(env, napi_invalid_arg, "invalid env"); if (!is_object_type((ant_value_t)arraybuffer)) return napi_set_last(env, napi_arraybuffer_expected, "arraybuffer expected"); - ant_value_t slot = js_get_slot(nenv->js, (ant_value_t)arraybuffer, SLOT_BUFFER); + ant_value_t slot = js_get_slot((ant_value_t)arraybuffer, SLOT_BUFFER); if (vtype(slot) != T_NUM) return napi_set_last(env, napi_arraybuffer_expected, "arraybuffer expected"); ArrayBufferData *ab = (ArrayBufferData *)(uintptr_t)js_getnum(slot); @@ -1461,7 +1461,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_get_property( ant_value_t k = (ant_value_t)key; if (vtype(k) == T_SYMBOL) { ant_offset_t off = lkp_sym_proto(nenv->js, (ant_value_t)object, (ant_offset_t)vdata(k)); - *result = (napi_value)(off ? resolveprop(nenv->js, mkval(T_PROP, off)) : js_mkundef()); + *result = (napi_value)(off ? js_propref_load(nenv->js, off) : js_mkundef()); return napi_set_last(env, napi_ok, NULL); } @@ -1706,13 +1706,14 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_define_properties( } int flags = napi_desc_flags(p->attributes); + ant_value_t desc_obj = js_as_obj((ant_value_t)object); if (is_symbol) { - if (p->getter) js_set_sym_getter_desc(nenv->js, (ant_value_t)object, key, (ant_value_t)getter_fn, flags); - if (p->setter) js_set_sym_setter_desc(nenv->js, (ant_value_t)object, key, (ant_value_t)setter_fn, flags); + if (p->getter) js_set_sym_getter_desc(nenv->js, desc_obj, key, (ant_value_t)getter_fn, flags); + if (p->setter) js_set_sym_setter_desc(nenv->js, desc_obj, key, (ant_value_t)setter_fn, flags); } else { js_set_accessor_desc( nenv->js, - (ant_value_t)object, + desc_obj, key_str, key_len, p->getter ? (ant_value_t)getter_fn : js_mkundef(), @@ -1729,7 +1730,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_define_properties( js_set_sym(nenv->js, (ant_value_t)object, key, value); } else { js_set(nenv->js, (ant_value_t)object, key_str, value); - js_set_descriptor(nenv->js, (ant_value_t)object, key_str, key_len, napi_desc_flags(p->attributes)); + js_set_descriptor(nenv->js, js_as_obj((ant_value_t)object), key_str, key_len, napi_desc_flags(p->attributes)); } } @@ -1757,6 +1758,8 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_define_class( if (st != napi_ok) return st; ant_value_t ctor = (ant_value_t)*result; + js_mark_constructor(ctor, true); + ant_value_t proto = js_get(nenv->js, ctor, "prototype"); if (!is_object_type(proto)) { proto = js_mkobj(nenv->js); @@ -1850,7 +1853,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_is_arraybuffer( if (!nenv || !nenv->js || !result) return napi_set_last(env, napi_invalid_arg, "invalid argument"); if (!is_object_type((ant_value_t)value)) { *result = false; return napi_set_last(env, napi_ok, NULL); } - ant_value_t slot = js_get_slot(nenv->js, (ant_value_t)value, SLOT_BUFFER); + ant_value_t slot = js_get_slot((ant_value_t)value, SLOT_BUFFER); if (vtype(slot) != T_NUM) { *result = false; } else { @@ -1902,7 +1905,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_is_dataview( if (!is_object_type((ant_value_t)value)) { *result = false; } else { - ant_value_t slot = js_get_slot(nenv->js, (ant_value_t)value, SLOT_DATA); + ant_value_t slot = js_get_slot((ant_value_t)value, SLOT_DATA); ant_value_t byte_len = js_get(nenv->js, (ant_value_t)value, "byteLength"); *result = vtype(slot) == T_NUM && vtype(byte_len) == T_NUM; } @@ -1919,7 +1922,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_is_error( if (!is_object_type((ant_value_t)value)) { *result = false; } else { - ant_value_t et = js_get_slot(nenv->js, (ant_value_t)value, SLOT_ERR_TYPE); + ant_value_t et = js_get_slot((ant_value_t)value, SLOT_ERR_TYPE); *result = vtype(et) == T_NUM; } return napi_set_last(env, napi_ok, NULL); @@ -2005,7 +2008,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_new_instance( ant_value_t ctor = (ant_value_t)constructor; ant_value_t obj = js_mkobj(nenv->js); ant_value_t proto = js_get(nenv->js, ctor, "prototype"); - if (is_object_type(proto)) js_set_proto(nenv->js, obj, proto); + if (is_object_type(proto)) js_set_proto_init(obj, proto); ant_value_t saved = nenv->js->new_target; nenv->js->new_target = ctor; @@ -2235,7 +2238,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_remove_wrap( if (result) *result = entry->native_object; HASH_DEL(g_napi_wraps, entry); free(entry); - js_set_slot(nenv->js, (ant_value_t)js_object, SLOT_NAPI_WRAP_ID, js_mkundef()); + js_set_slot((ant_value_t)js_object, SLOT_NAPI_WRAP_ID, js_mkundef()); return napi_set_last(env, napi_ok, NULL); } @@ -2396,7 +2399,7 @@ NAPI_EXTERN napi_status NAPI_CDECL napi_create_threadsafe_function( tsfn->context = context; tsfn->max_queue_size = max_queue_size; tsfn->thread_count = initial_thread_count > 0 ? initial_thread_count : 1; - tsfn->func_root = js_root(nenv->js, (ant_value_t)func); + tsfn->func_val = (ant_value_t)func; uv_mutex_init(&tsfn->mutex); int rc = uv_async_init(uv_default_loop(), &tsfn->async, napi_tsfn_async_cb); diff --git a/src/modules/navigator.c b/src/modules/navigator.c index 003932a..04cb61e 100644 --- a/src/modules/navigator.c +++ b/src/modules/navigator.c @@ -18,6 +18,7 @@ #include "silver/engine.h" #include "modules/navigator.h" #include "modules/symbol.h" +#include "gc/modules.h" typedef enum { LOCK_MODE_EXCLUSIVE, @@ -124,15 +125,15 @@ static ant_value_t make_lock_handler(ant_t *js, ant_value_t cfunc, ant_value_t l ant_value_t data_obj = js_mkobj(js); js_set(js, data_obj, "lockName", lock_name); js_set(js, data_obj, "outerPromise", outer_promise); - js_set_slot(js, fn_obj, SLOT_DATA, data_obj); - js_set_slot(js, fn_obj, SLOT_CFUNC, cfunc); + js_set_slot(fn_obj, SLOT_DATA, data_obj); + js_set_slot(fn_obj, SLOT_CFUNC, cfunc); return js_obj_to_func(fn_obj); } static ant_value_t lock_then_handler(ant_t *js, ant_value_t *args, int nargs) { ant_value_t current_func = js_getcurrentfunc(js); - ant_value_t data_obj = js_get_slot(js, current_func, SLOT_DATA); + ant_value_t data_obj = js_get_slot(current_func, SLOT_DATA); ant_value_t lock_name_val = js_get(js, data_obj, "lockName"); ant_value_t outer_promise = js_get(js, data_obj, "outerPromise"); @@ -150,7 +151,7 @@ static ant_value_t lock_then_handler(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t lock_catch_handler(ant_t *js, ant_value_t *args, int nargs) { ant_value_t current_func = js_getcurrentfunc(js); - ant_value_t data_obj = js_get_slot(js, current_func, SLOT_DATA); + ant_value_t data_obj = js_get_slot(current_func, SLOT_DATA); ant_value_t lock_name_val = js_get(js, data_obj, "lockName"); ant_value_t outer_promise = js_get(js, data_obj, "outerPromise"); @@ -391,9 +392,10 @@ void init_navigator_module(void) { js_set(js, js_glob(js), "navigator", navigator_obj); } -void navigator_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_navigator(ant_t *js, gc_mark_fn mark) { for (lock_request_t *req = pending_requests; req; req = req->next) { - op_val(ctx, &req->callback); - op_val(ctx, &req->promise); + mark(js, req->callback); + mark(js, req->promise); } } + diff --git a/src/modules/observable.c b/src/modules/observable.c index 220d6e8..3c5d979 100644 --- a/src/modules/observable.c +++ b/src/modules/observable.c @@ -17,16 +17,16 @@ static inline bool is_callable(ant_value_t val) { } static bool subscription_closed(ant_t *js, ant_value_t subscription) { - ant_value_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); + ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); return vtype(observer) == T_UNDEF; } static void cleanup_subscription(ant_t *js, ant_value_t subscription) { - ant_value_t cleanup = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP); + ant_value_t cleanup = js_get_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP); if (vtype(cleanup) == T_UNDEF) return; if (!is_callable(cleanup)) return; - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); + js_set_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); ant_value_t result = sv_vm_call(js->vm, js, cleanup, js_mkundef(), NULL, 0, NULL, false); if (vtype(result) == T_ERR) fprintf(stderr, "Error in subscription cleanup: %s\n", js_str(js, result)); @@ -34,8 +34,8 @@ static void cleanup_subscription(ant_t *js, ant_value_t subscription) { static ant_value_t create_subscription(ant_t *js, ant_value_t observer) { ant_value_t subscription = js_mkobj(js); - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, observer); - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); + js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, observer); + js_set_slot(subscription, SLOT_SUBSCRIPTION_CLEANUP, js_mkundef()); js_set_sym(js, subscription, get_toStringTag_sym(), js_mkstr(js, "Subscription", 12)); return subscription; } @@ -60,7 +60,7 @@ static ant_value_t js_subscription_unsubscribe(ant_t *js, ant_value_t *args, int if (subscription_closed(js, subscription)) return js_mkundef(); - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); + js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); cleanup_subscription(js, subscription); return js_mkundef(); @@ -80,7 +80,7 @@ static ant_value_t js_subobs_get_closed(ant_t *js, ant_value_t *args, int nargs) return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.closed getter called on non-object"); } - ant_value_t subscription = js_get_slot(js, O, SLOT_DATA); + ant_value_t subscription = js_get_slot(O, SLOT_DATA); if (!is_special_object(subscription)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); } @@ -95,14 +95,14 @@ static ant_value_t js_subobs_next(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.next called on non-object"); } - ant_value_t subscription = js_get_slot(js, O, SLOT_DATA); + ant_value_t subscription = js_get_slot(O, SLOT_DATA); if (!is_special_object(subscription)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); } if (subscription_closed(js, subscription)) return js_mkundef(); - ant_value_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); + ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); if (!is_special_object(observer)) return js_mkundef(); ant_value_t nextMethod = js_get(js, observer, "next"); @@ -123,15 +123,15 @@ static ant_value_t js_subobs_error(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.error called on non-object"); } - ant_value_t subscription = js_get_slot(js, O, SLOT_DATA); + ant_value_t subscription = js_get_slot(O, SLOT_DATA); if (!is_special_object(subscription)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); } if (subscription_closed(js, subscription)) return js_mkundef(); - ant_value_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); + ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); + js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); if (is_special_object(observer)) { ant_value_t errorMethod = js_get(js, observer, "error"); @@ -155,15 +155,15 @@ static ant_value_t js_subobs_complete(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "SubscriptionObserver.complete called on non-object"); } - ant_value_t subscription = js_get_slot(js, O, SLOT_DATA); + ant_value_t subscription = js_get_slot(O, SLOT_DATA); if (!is_special_object(subscription)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Invalid SubscriptionObserver"); } if (subscription_closed(js, subscription)) return js_mkundef(); - ant_value_t observer = js_get_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER); - js_set_slot(js, subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); + ant_value_t observer = js_get_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER); + js_set_slot(subscription, SLOT_SUBSCRIPTION_OBSERVER, js_mkundef()); if (is_special_object(observer)) { ant_value_t completeMethod = js_get(js, observer, "complete"); @@ -180,7 +180,7 @@ static ant_value_t js_subobs_complete(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t create_subscription_observer(ant_t *js, ant_value_t subscription) { ant_value_t subobs = js_mkobj(js); - js_set_slot(js, subobs, SLOT_DATA, subscription); + js_set_slot(subobs, SLOT_DATA, subscription); js_set(js, subobs, "next", js_mkfun(js_subobs_next)); js_set(js, subobs, "error", js_mkfun(js_subobs_error)); js_set(js, subobs, "complete", js_mkfun(js_subobs_complete)); @@ -195,7 +195,7 @@ static ant_value_t create_subscription_observer(ant_t *js, ant_value_t subscript static ant_value_t js_cleanup_fn(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t F = js_getcurrentfunc(js); - ant_value_t subscription = js_get_slot(js, F, SLOT_DATA); + ant_value_t subscription = js_get_slot(F, SLOT_DATA); if (!is_special_object(subscription)) return js_mkundef(); @@ -222,8 +222,8 @@ static ant_value_t execute_subscriber(ant_t *js, ant_value_t subscriber, ant_val } ant_value_t cleanupFunction = js_mkobj(js); - js_set_slot(js, cleanupFunction, SLOT_DATA, subscriberResult); - js_set_slot(js, cleanupFunction, SLOT_CFUNC, js_mkfun(js_cleanup_fn)); + js_set_slot(cleanupFunction, SLOT_DATA, subscriberResult); + js_set_slot(cleanupFunction, SLOT_CFUNC, js_mkfun(js_cleanup_fn)); return js_obj_to_func(cleanupFunction); } @@ -237,7 +237,7 @@ static ant_value_t js_observable_subscribe(ant_t *js, ant_value_t *args, int nar return js_mkerr_typed(js, JS_ERR_TYPE, "Observable.prototype.subscribe called on non-object"); } - ant_value_t subscriber = js_get_slot(js, O, SLOT_OBSERVABLE_SUBSCRIBER); + ant_value_t subscriber = js_get_slot(O, SLOT_OBSERVABLE_SUBSCRIBER); if (!is_callable(subscriber)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Observable has no [[Subscriber]] internal slot"); } @@ -281,7 +281,7 @@ static ant_value_t js_observable_subscribe(ant_t *js, ant_value_t *args, int nar ant_value_t error_args[1] = {thrown_error}; ant_value_t error_method = js_get(js, subscriptionObserver, "error"); if (is_callable(error_method)) sv_vm_call(js->vm, js, error_method, subscriptionObserver, error_args, 1, NULL, false); - } else js_set_slot(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, subscriberResult); + } else js_set_slot_wb(js, subscription, SLOT_SUBSCRIPTION_CLEANUP, subscriberResult); if (subscription_closed(js, subscription)) cleanup_subscription(js, subscription); @@ -306,20 +306,20 @@ static ant_value_t js_observable_constructor(ant_t *js, ant_value_t *args, int n ant_value_t proto = js_get_ctor_proto(js, "Observable", 10); ant_value_t observable = js_mkobj(js); - js_set_proto(js, observable, proto); - js_set_slot(js, observable, SLOT_OBSERVABLE_SUBSCRIBER, subscriber); + js_set_proto_init(observable, proto); + js_set_slot(observable, SLOT_OBSERVABLE_SUBSCRIBER, subscriber); return observable; } static ant_value_t js_of_subscriber(ant_t *js, ant_value_t *args, int nargs) { ant_value_t F = js_getcurrentfunc(js); - ant_value_t items = js_get_slot(js, F, SLOT_DATA); + ant_value_t items = js_get_slot(F, SLOT_DATA); if (nargs < 1) return js_mkundef(); ant_value_t observer = args[0]; - ant_value_t subscription = js_get_slot(js, observer, SLOT_DATA); + ant_value_t subscription = js_get_slot(observer, SLOT_DATA); ant_value_t length_val = js_get(js, items, "length"); int length = (vtype(length_val) == T_NUM) ? (int)js_getnum(length_val) : 0; @@ -357,7 +357,7 @@ static ant_value_t js_observable_of(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_from_delegating(ant_t *js, ant_value_t *args, int nargs) { ant_value_t F = js_getcurrentfunc(js); - ant_value_t observable = js_get_slot(js, F, SLOT_DATA); + ant_value_t observable = js_get_slot(F, SLOT_DATA); if (!is_special_object(observable)) return js_mkundef(); ant_value_t subscribe = js_get(js, observable, "subscribe"); @@ -370,7 +370,7 @@ static ant_value_t js_from_delegating(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_from_iteration(ant_t *js, ant_value_t *args, int nargs) { ant_value_t F = js_getcurrentfunc(js); - ant_value_t data = js_get_slot(js, F, SLOT_DATA); + ant_value_t data = js_get_slot(F, SLOT_DATA); ant_value_t iterable = js_get(js, data, "iterable"); ant_value_t iteratorMethod = js_get(js, data, "iteratorMethod"); @@ -378,7 +378,7 @@ static ant_value_t js_from_iteration(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t observer = args[0]; - ant_value_t subscription = js_get_slot(js, observer, SLOT_DATA); + ant_value_t subscription = js_get_slot(observer, SLOT_DATA); if (!is_callable(iteratorMethod)) { return js_mkerr_typed(js, JS_ERR_TYPE, "Object is not iterable"); @@ -437,7 +437,7 @@ static ant_value_t js_observable_from(ant_t *js, ant_value_t *args, int nargs) { return js_mkerr_typed(js, JS_ERR_TYPE, "@@observable must return an object"); } - ant_value_t existing_subscriber = js_get_slot(js, observable, SLOT_OBSERVABLE_SUBSCRIBER); + ant_value_t existing_subscriber = js_get_slot(observable, SLOT_OBSERVABLE_SUBSCRIBER); if (is_callable(existing_subscriber)) return observable; ant_value_t subscriber_func = js_heavy_mkfun(js, js_from_delegating, observable); @@ -479,7 +479,7 @@ void init_observable_module(void) { js_set_sym(js, observable_proto, get_observable_sym(), js_mkfun(js_observable_symbol_observable)); js_set_sym(js, observable_proto, get_toStringTag_sym(), js_mkstr(js, "Observable", 10)); - js_set_slot(js, observable_ctor, SLOT_CFUNC, js_mkfun(js_observable_constructor)); + js_set_slot(observable_ctor, SLOT_CFUNC, js_mkfun(js_observable_constructor)); js_mkprop_fast(js, observable_ctor, "prototype", 9, observable_proto); js_mkprop_fast(js, observable_ctor, "name", 4, ANT_STRING("Observable")); js_set_descriptor(js, observable_ctor, "name", 4, 0); diff --git a/src/modules/process.c b/src/modules/process.c index 70dca38..48a81e0 100644 --- a/src/modules/process.c +++ b/src/modules/process.c @@ -34,6 +34,7 @@ #include "descriptors.h" #include "runtime.h" #include "silver/engine.h" +#include "gc/modules.h" #include "modules/process.h" #include "modules/symbol.h" @@ -67,9 +68,16 @@ typedef struct { static int max_listeners = DEFAULT_MAX_LISTENERS; static ProcessEventType *process_events = NULL; -static ProcessEventType *stdin_events = NULL; -static ProcessEventType *stdout_events = NULL; -static ProcessEventType *stderr_events = NULL; +typedef struct { + uv_signal_t handle; + int signum; + UT_hash_handle hh; +} SignalHandle; + +static SignalHandle *signal_handles = NULL; +static ProcessEventType *stdin_events = NULL; +static ProcessEventType *stdout_events = NULL; +static ProcessEventType *stderr_events = NULL; typedef struct { uv_tty_t tty; @@ -105,89 +113,38 @@ static void init_signal_map(void) { static bool initialized = false; if (initialized) return; + #define S(sig) { #sig, sig, {0}, {0} } static SignalEntry entries[] = { -#ifdef SIGHUP - { "SIGHUP", SIGHUP, {0}, {0} }, -#endif -#ifdef SIGINT - { "SIGINT", SIGINT, {0}, {0} }, -#endif -#ifdef SIGQUIT - { "SIGQUIT", SIGQUIT, {0}, {0} }, -#endif -#ifdef SIGILL - { "SIGILL", SIGILL, {0}, {0} }, -#endif -#ifdef SIGTRAP - { "SIGTRAP", SIGTRAP, {0}, {0} }, -#endif -#ifdef SIGABRT - { "SIGABRT", SIGABRT, {0}, {0} }, -#endif -#ifdef SIGBUS - { "SIGBUS", SIGBUS, {0}, {0} }, -#endif -#ifdef SIGFPE - { "SIGFPE", SIGFPE, {0}, {0} }, -#endif -#ifdef SIGUSR1 - { "SIGUSR1", SIGUSR1, {0}, {0} }, -#endif -#ifdef SIGUSR2 - { "SIGUSR2", SIGUSR2, {0}, {0} }, -#endif -#ifdef SIGSEGV - { "SIGSEGV", SIGSEGV, {0}, {0} }, -#endif -#ifdef SIGPIPE - { "SIGPIPE", SIGPIPE, {0}, {0} }, -#endif -#ifdef SIGALRM - { "SIGALRM", SIGALRM, {0}, {0} }, -#endif -#ifdef SIGTERM - { "SIGTERM", SIGTERM, {0}, {0} }, -#endif -#ifdef SIGCHLD - { "SIGCHLD", SIGCHLD, {0}, {0} }, -#endif -#ifdef SIGCONT - { "SIGCONT", SIGCONT, {0}, {0} }, -#endif -#ifdef SIGTSTP - { "SIGTSTP", SIGTSTP, {0}, {0} }, -#endif -#ifdef SIGTTIN - { "SIGTTIN", SIGTTIN, {0}, {0} }, -#endif -#ifdef SIGTTOU - { "SIGTTOU", SIGTTOU, {0}, {0} }, -#endif -#ifdef SIGURG - { "SIGURG", SIGURG, {0}, {0} }, -#endif -#ifdef SIGXCPU - { "SIGXCPU", SIGXCPU, {0}, {0} }, -#endif -#ifdef SIGXFSZ - { "SIGXFSZ", SIGXFSZ, {0}, {0} }, -#endif -#ifdef SIGVTALRM - { "SIGVTALRM", SIGVTALRM, {0}, {0} }, -#endif -#ifdef SIGPROF - { "SIGPROF", SIGPROF, {0}, {0} }, -#endif -#ifdef SIGWINCH - { "SIGWINCH", SIGWINCH, {0}, {0} }, -#endif -#ifdef SIGIO - { "SIGIO", SIGIO, {0}, {0} }, -#endif -#ifdef SIGSYS - { "SIGSYS", SIGSYS, {0}, {0} }, -#endif + S(SIGHUP), S(SIGINT), S(SIGQUIT), S(SIGILL), + S(SIGTRAP), S(SIGABRT), S(SIGFPE), S(SIGKILL), + S(SIGBUS), S(SIGSEGV), S(SIGSYS), S(SIGPIPE), + S(SIGALRM), S(SIGTERM), S(SIGURG), S(SIGSTOP), + S(SIGTSTP), S(SIGCONT), S(SIGCHLD), S(SIGTTIN), + S(SIGTTOU), S(SIGXCPU), S(SIGXFSZ), S(SIGVTALRM), + S(SIGPROF), S(SIGUSR1), S(SIGUSR2), + #ifdef SIGEMT + S(SIGEMT), + #endif + #ifdef SIGIO + S(SIGIO), + #endif + #ifdef SIGWINCH + S(SIGWINCH), + #endif + #ifdef SIGINFO + S(SIGINFO), + #endif + #ifdef SIGPWR + S(SIGPWR), + #endif + #ifdef SIGSTKFLT + S(SIGSTKFLT), + #endif + #ifdef SIGPOLL + S(SIGPOLL), + #endif }; + #undef S for (size_t i = 0; i < sizeof(entries) / sizeof(entries[0]); i++) { HASH_ADD_KEYPTR(hh_name, signals_by_name, entries[i].name, strlen(entries[i].name), &entries[i]); @@ -261,6 +218,43 @@ static void check_listener_warning(const char *event) { ); } +static void uv_signal_handler(uv_signal_t *handle, int signum) { + const char *name = get_signal_name(signum); + if (name) { + ant_value_t sig_arg = js_mkstr(rt->js, name, strlen(name)); + emit_process_event(name, &sig_arg, 1); + } +} + +static void start_signal_watch(int signum) { + SignalHandle *sh = NULL; + HASH_FIND_INT(signal_handles, &signum, sh); + if (sh) return; + + sh = malloc(sizeof(SignalHandle)); + sh->signum = signum; + uv_signal_init(uv_default_loop(), &sh->handle); + uv_signal_start(&sh->handle, uv_signal_handler, signum); + uv_unref((uv_handle_t *)&sh->handle); + HASH_ADD_INT(signal_handles, signum, sh); +} + +static void on_signal_handle_close(uv_handle_t *handle) { + SignalHandle *sh = (SignalHandle *)handle; + free(sh); +} + +static void stop_signal_watch(int signum) { + SignalHandle *sh = NULL; + HASH_FIND_INT(signal_handles, &signum, sh); + if (!sh) return; + + HASH_DEL(signal_handles, sh); + uv_signal_stop(&sh->handle); + uv_close((uv_handle_t *)&sh->handle, on_signal_handle_close); +} + + void emit_process_event(const char *event_type, ant_value_t *args, int nargs) { if (!rt->js) return; @@ -283,21 +277,11 @@ void emit_process_event(const char *event_type, ant_value_t *args, int nargs) { if (evt->listener_count == 0 || evt->free_deferred) { int signum = get_signal_number(event_type); - if (signum > 0) signal(signum, SIG_DFL); + if (signum > 0) stop_signal_watch(signum); if (evt->emitting == 0) free_event_type(&process_events, evt); } } -static void process_signal_handler(int signum) { - const char *name = get_signal_name(signum); - if (name) { - ant_value_t sig_arg = js_mkstr(rt->js, name, strlen(name)); - emit_process_event(name, &sig_arg, 1); - } -} - - - static void emit_stdio_event(ProcessEventType **events, const char *event_type, ant_value_t *args, int nargs) { if (!rt->js) return; ProcessEventType *evt = NULL; @@ -1489,7 +1473,7 @@ static ant_value_t process_on(ant_t *js, ant_value_t *args, int nargs) { int signum = get_signal_number(event); if (signum > 0) { - signal(signum, process_signal_handler); + start_signal_watch(signum); } ProcessEventType *evt = find_or_create_event(&process_events, event); @@ -1515,7 +1499,7 @@ static ant_value_t process_once(ant_t *js, ant_value_t *args, int nargs) { int signum = get_signal_number(event); if (signum > 0) { - signal(signum, process_signal_handler); + start_signal_watch(signum); } ProcessEventType *evt = find_or_create_event(&process_events, event); @@ -1554,7 +1538,7 @@ static ant_value_t process_off(ant_t *js, ant_value_t *args, int nargs) { if (evt->listener_count == 0) { int signum = get_signal_number(event); - if (signum > 0) signal(signum, SIG_DFL); + if (signum > 0) stop_signal_watch(signum); } return process_obj; @@ -1570,7 +1554,7 @@ static ant_value_t process_remove_all_listeners(ant_t *js, ant_value_t *args, in HASH_FIND_STR(process_events, event, evt); if (evt) { int signum = get_signal_number(event); - if (signum > 0) signal(signum, SIG_DFL); + if (signum > 0) stop_signal_watch(signum); free_event_type(&process_events, evt); } } @@ -1578,7 +1562,7 @@ static ant_value_t process_remove_all_listeners(ant_t *js, ant_value_t *args, in ProcessEventType *evt, *tmp; HASH_ITER(hh, process_events, evt, tmp) { int signum = get_signal_number(evt->event_type); - if (signum > 0) signal(signum, SIG_DFL); + if (signum > 0) stop_signal_watch(signum); free_event_type(&process_events, evt); } } @@ -1680,14 +1664,14 @@ void init_process_module() { ant_value_t process_obj = js_mkobj(js); ant_value_t env_obj = js_mkobj(js); - js_set_proto(js, process_obj, process_proto); + js_set_proto_init(process_obj, process_proto); load_dotenv_file(js, env_obj); - js_set_keys(js, env_obj, env_keys); + js_set_keys(env_obj, env_keys); - js_set_getter(js, env_obj, env_getter); - js_set_setter(js, env_obj, env_setter); - js_set_deleter(js, env_obj, env_deleter); + js_set_getter(env_obj, env_getter); + js_set_setter(env_obj, env_setter); + js_set_deleter(env_obj, env_deleter); js_set(js, env_obj, "toObject", js_mkfun(env_to_object)); js_set(js, env_obj, "toString", js_mkfun(env_toString)); @@ -1758,7 +1742,7 @@ void init_process_module() { js_set_sym(js, stdin_proto, get_toStringTag_sym(), js_mkstr(js, "ReadStream", 10)); ant_value_t stdin_obj = js_mkobj(js); - js_set_proto(js, stdin_obj, stdin_proto); + js_set_proto_init(stdin_obj, stdin_proto); js_set(js, stdin_obj, "isTTY", js_bool(stdin_is_tty())); js_set(js, process_obj, "stdin", stdin_obj); @@ -1773,7 +1757,7 @@ void init_process_module() { js_set_sym(js, stdout_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); ant_value_t stdout_obj = js_mkobj(js); - js_set_proto(js, stdout_obj, stdout_proto); + js_set_proto_init(stdout_obj, stdout_proto); js_set(js, stdout_obj, "isTTY", js_bool(stdout_is_tty())); js_set_getter_desc(js, stdout_obj, "rows", 4, js_mkfun(js_stdout_rows_getter), JS_DESC_E | JS_DESC_C); js_set_getter_desc(js, stdout_obj, "columns", 7, js_mkfun(js_stdout_columns_getter), JS_DESC_E | JS_DESC_C); @@ -1789,34 +1773,28 @@ void init_process_module() { js_set_sym(js, stderr_proto, get_toStringTag_sym(), js_mkstr(js, "WriteStream", 11)); ant_value_t stderr_obj = js_mkobj(js); - js_set_proto(js, stderr_obj, stderr_proto); + js_set_proto_init(stderr_obj, stderr_proto); js_set(js, stderr_obj, "isTTY", js_bool(stderr_is_tty())); js_set(js, process_obj, "stderr", stderr_obj); js_set(js, global, "process", process_obj); } -#define GC_OP_EVENTS(events) do { \ - ProcessEventType *evt, *tmp; \ - HASH_ITER(hh, events, evt, tmp) { \ - for (int i = 0; i < evt->listener_count; i++) \ - op_val(ctx, &evt->listeners[i].listener); \ - } \ -} while(0) - -void process_gc_update_roots(GC_OP_VAL_ARGS) { - GC_OP_EVENTS(process_events); - GC_OP_EVENTS(stdin_events); - GC_OP_EVENTS(stdout_events); - GC_OP_EVENTS(stderr_events); -} - -#undef GC_OP_EVENTS bool has_active_stdin(void) { return stdin_state.reading; } +void gc_mark_process(ant_t *js, gc_mark_fn mark) { + ProcessEventType *tables[] = {process_events, stdin_events, stdout_events, stderr_events}; + for (int t = 0; t < 4; t++) { + ProcessEventType *evt, *tmp; + HASH_ITER(hh, tables[t], evt, tmp) { + for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); + } + } +} + void process_enable_keypress_events(void) { stdin_state.keypress_enabled = true; stdin_state.escape_state = 0; diff --git a/src/modules/readline.c b/src/modules/readline.c index 174e6f2..aeefb3c 100644 --- a/src/modules/readline.c +++ b/src/modules/readline.c @@ -30,6 +30,7 @@ #include "tty_ctrl.h" #include "silver/engine.h" +#include "gc/modules.h" #include "modules/readline.h" #include "modules/process.h" #include "modules/symbol.h" @@ -1266,22 +1267,6 @@ bool has_active_readline_interfaces(void) { return false; } -void readline_gc_update_roots(GC_OP_VAL_ARGS) { - rl_interface_t *iface, *tmp; - HASH_ITER(hh, interfaces, iface, tmp) { - op_val(ctx, &iface->input_stream); - op_val(ctx, &iface->output_stream); - op_val(ctx, &iface->completer); - op_val(ctx, &iface->js_obj); - op_val(ctx, &iface->pending_question_resolve); - op_val(ctx, &iface->pending_question_reject); - - RLEventType *evt, *evt_tmp; - HASH_ITER(hh, iface->events, evt, evt_tmp) { - for (int i = 0; i < evt->listener_count; i++) op_val(ctx, &evt->listeners[i].listener); - } - } -} ant_value_t readline_library(ant_t *js) { ant_value_t lib = js_mkobj(js); @@ -1310,3 +1295,20 @@ ant_value_t readline_promises_library(ant_t *js) { return lib; } + +void gc_mark_readline(ant_t *js, gc_mark_fn mark) { + rl_interface_t *iface, *tmp; + HASH_ITER(hh, interfaces, iface, tmp) { + mark(js, iface->input_stream); + mark(js, iface->output_stream); + mark(js, iface->completer); + mark(js, iface->js_obj); + mark(js, iface->pending_question_resolve); + mark(js, iface->pending_question_reject); + + RLEventType *evt, *evt_tmp; + HASH_ITER(hh, iface->events, evt, evt_tmp) { + for (int i = 0; i < evt->listener_count; i++) mark(js, evt->listeners[i].listener); + } + } +} diff --git a/src/modules/reflect.c b/src/modules/reflect.c index 2563041..290d7fb 100644 --- a/src/modules/reflect.c +++ b/src/modules/reflect.c @@ -79,7 +79,8 @@ static ant_value_t reflect_delete_property(ant_t *js, ant_value_t *args, int nar char *key_str = js_getstr(js, key, NULL); if (!key_str) return js_false; - bool deleted = js_del(js, target, key_str); + ant_value_t del_result = js_delete_prop(js, target, key_str, strlen(key_str)); + bool deleted = !is_err(del_result) && js_truthy(js, del_result); return js_bool(deleted); } @@ -144,7 +145,7 @@ static ant_value_t reflect_construct(ant_t *js, ant_value_t *args, int nargs) { ant_value_t new_obj = js_mkobj(js); ant_value_t proto = js_get(js, new_target, "prototype"); - if (vtype(proto) == T_OBJ) js_set_proto(js, new_obj, proto); + if (vtype(proto) == T_OBJ) js_set_proto_init(new_obj, proto); ant_value_t saved_new_target = js->new_target; js->new_target = new_target; @@ -225,24 +226,7 @@ static ant_value_t reflect_get_own_property_descriptor(ant_t *js, ant_value_t *a static ant_value_t reflect_define_property(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 3) return js_false; - - ant_value_t target = args[0]; - ant_value_t key = args[1]; - ant_value_t descriptor = args[2]; - - int t = vtype(target); - if (t != T_OBJ && t != T_FUNC) return js_false; - - if (vtype(key) != T_STR) return js_false; - if (!is_special_object(descriptor)) return js_false; - - char *key_str = js_getstr(js, key, NULL); - if (!key_str) return js_false; - - ant_value_t value = js_get(js, descriptor, "value"); - js_set(js, target, key_str, value); - - return js_true; + return js_define_property(js, args[0], args[1], args[2], true); } static ant_value_t reflect_get_prototype_of(ant_t *js, ant_value_t *args, int nargs) { @@ -271,34 +255,37 @@ static ant_value_t reflect_set_prototype_of(ant_t *js, ant_value_t *args, int na int pt = vtype(proto); if (pt != T_OBJ && pt != T_FUNC && pt != T_NULL) return js_false; - js_set_proto(js, target, proto); + js_set_proto_wb(js, target, proto); return js_true; } static ant_value_t reflect_is_extensible(ant_t *js, ant_value_t *args, int nargs) { + (void)js; if (nargs < 1) return js_false; ant_value_t target = args[0]; int t = vtype(target); if (t != T_OBJ && t != T_FUNC) return js_false; - - if (js_get_slot(js, target, SLOT_EXTENSIBLE) == js_false) return js_false; - if (js_get_slot(js, target, SLOT_FROZEN) == js_true) return js_false; - if (js_get_slot(js, target, SLOT_SEALED) == js_true) return js_false; - - return js_true; + + ant_object_t *obj = js_obj_ptr(js_as_obj(target)); + if (!obj) return js_false; + if (obj->frozen || obj->sealed) return js_false; + return js_bool(obj->extensible); } static ant_value_t reflect_prevent_extensions(ant_t *js, ant_value_t *args, int nargs) { + (void)js; if (nargs < 1) return js_false; ant_value_t target = args[0]; int t = vtype(target); if (t != T_OBJ && t != T_FUNC) return js_false; - - js_set_slot(js, target, SLOT_EXTENSIBLE, js_false); + + ant_object_t *obj = js_obj_ptr(js_as_obj(target)); + if (!obj) return js_false; + obj->extensible = 0; return js_true; } diff --git a/src/modules/regex.c b/src/modules/regex.c index da4027d..2ce9592 100644 --- a/src/modules/regex.c +++ b/src/modules/regex.c @@ -3,24 +3,23 @@ #include #include "ant.h" +#include "utf8.h" #include "errors.h" #include "runtime.h" #include "internal.h" -#include "silver/engine.h" -#include "arena.h" #include "utils.h" -#include "gc.h" -#include "utf8.h" +#include "escape.h" #include "descriptors.h" -#include "escape.h" +#include "silver/engine.h" #include "modules/regex.h" #include "modules/symbol.h" +#include "gc/objects.h" #include typedef struct { - ant_offset_t obj_offset; + ant_object_t *obj; pcre2_code *code; pcre2_match_data *match_data; } regex_cache_entry_t; @@ -400,7 +399,7 @@ static ant_value_t regexp_species_construct(ant_t *js, ant_value_t rx, ant_value ant_value_t proto = js_get(js, ctor, "prototype"); if (is_err(proto)) return proto; - if (is_object_type(proto)) js_set_proto(js, seed, proto); + if (is_object_type(proto)) js_set_proto_init(seed, proto); ant_value_t saved = js->new_target; js->new_target = ctor; @@ -417,14 +416,14 @@ static ant_value_t regexp_species_construct(ant_t *js, ant_value_t rx, ant_value static ant_value_t regexp_exec_abstract(ant_t *js, ant_value_t rx, ant_value_t str); static ant_value_t builtin_regexp_exec(ant_t *js, ant_value_t *args, int nargs); -static regex_cache_entry_t *regex_cache_lookup(ant_offset_t obj_offset) { +static regex_cache_entry_t *regex_cache_lookup(ant_object_t *obj) { for (size_t i = 0; i < regex_cache_count; i++) { - if (regex_cache[i].obj_offset == obj_offset) return ®ex_cache[i]; + if (regex_cache[i].obj == obj) return ®ex_cache[i]; } return NULL; } -static regex_cache_entry_t *regex_cache_insert(ant_offset_t obj_offset, pcre2_code *code, pcre2_match_data *match_data) { +static regex_cache_entry_t *regex_cache_insert(ant_object_t *obj, pcre2_code *code, pcre2_match_data *match_data) { if (regex_cache_count >= regex_cache_cap) { size_t new_cap = regex_cache_cap ? regex_cache_cap * 2 : 64; regex_cache_entry_t *new_cache = realloc(regex_cache, new_cap * sizeof(regex_cache_entry_t)); @@ -433,7 +432,7 @@ static regex_cache_entry_t *regex_cache_insert(ant_offset_t obj_offset, pcre2_co regex_cache_cap = new_cap; } regex_cache_entry_t *entry = ®ex_cache[regex_cache_count++]; - entry->obj_offset = obj_offset; + entry->obj = obj; entry->code = code; entry->match_data = match_data; return entry; @@ -445,9 +444,9 @@ typedef struct { } compiled_regex_t; static bool regex_get_or_compile(ant_t *js, ant_value_t regexp_obj, compiled_regex_t *out) { - ant_offset_t obj_off = (ant_offset_t)vdata(regexp_obj); + ant_object_t *obj_ptr = js_obj_ptr(regexp_obj); - regex_cache_entry_t *cached = regex_cache_lookup(obj_off); + regex_cache_entry_t *cached = regex_cache_lookup(obj_ptr); if (cached) { out->code = cached->code; out->match_data = cached->match_data; @@ -456,19 +455,19 @@ static bool regex_get_or_compile(ant_t *js, ant_value_t regexp_obj, compiled_reg ant_offset_t source_off = lkp(js, regexp_obj, "source", 6); if (source_off == 0) return false; - ant_value_t source_val = resolveprop(js, mkval(T_PROP, source_off)); + ant_value_t source_val = js_propref_load(js, source_off); if (vtype(source_val) != T_STR) return false; ant_offset_t plen, poff = vstr(js, source_val, &plen); - const char *pattern_ptr = (char *)&js->mem[poff]; + const char *pattern_ptr = (char *)(uintptr_t)(poff); bool ignore_case = false, multiline = false, dotall = false, sticky = false, unicode = false; ant_offset_t flags_off = lkp(js, regexp_obj, "flags", 5); if (flags_off != 0) { - ant_value_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); + ant_value_t flags_val = js_propref_load(js, flags_off); if (vtype(flags_val) == T_STR) { ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *)&js->mem[foff]; + const char *flags_str = (char *)(uintptr_t)(foff); for (ant_offset_t i = 0; i < flen; i++) { if (flags_str[i] == 'i') ignore_case = true; if (flags_str[i] == 'm') multiline = true; @@ -495,7 +494,7 @@ static bool regex_get_or_compile(ant_t *js, ant_value_t regexp_obj, compiled_reg if (re == NULL) return false; pcre2_match_data *match_data = pcre2_match_data_create_from_pattern(re, NULL); - regex_cache_insert(obj_off, re, match_data); + regex_cache_insert(obj_ptr, re, match_data); out->code = re; out->match_data = match_data; @@ -531,9 +530,9 @@ static ant_value_t builtin_RegExp(ant_t *js, ant_value_t *args, int nargs) { ant_value_t regexp_proto = js_get_ctor_proto(js, "RegExp", 6); ant_value_t instance_proto = js_instance_proto_from_new_target(js, regexp_proto); - if (is_object_type(instance_proto)) js_set_proto(js, regexp_obj, instance_proto); + if (is_object_type(instance_proto)) js_set_proto_init(regexp_obj, instance_proto); if (vtype(js->new_target) == T_FUNC || vtype(js->new_target) == T_CFUNC) { - js_set_slot(js, regexp_obj, SLOT_CTOR, js->new_target); + js_set_slot(regexp_obj, SLOT_CTOR, js->new_target); } ant_value_t pattern = js_mkstr(js, "", 0); @@ -565,7 +564,7 @@ static ant_value_t builtin_RegExp(ant_t *js, ant_value_t *args, int nargs) { js_mkprop_fast(js, regexp_obj, "source", 6, pattern); ant_offset_t flags_len, flags_off = vstr(js, flags, &flags_len); - regexp_init_flags(js, regexp_obj, (const char *)&js->mem[flags_off], flags_len, true); + regexp_init_flags(js, regexp_obj, (const char *)(uintptr_t)(flags_off), flags_len, true); return regexp_obj; } @@ -579,15 +578,15 @@ static ant_value_t builtin_regexp_exec(ant_t *js, ant_value_t *args, int nargs) if (vtype(str_arg) != T_STR) return js_mknull(); ant_offset_t str_len, str_off = vstr(js, str_arg, &str_len); - const char *str_ptr = (char *)&js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); bool global_flag = false, sticky_flag = false; ant_offset_t flags_off = lkp(js, regexp, "flags", 5); if (flags_off != 0) { - ant_value_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); + ant_value_t flags_val = js_propref_load(js, flags_off); if (vtype(flags_val) == T_STR) { ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *)&js->mem[foff]; + const char *flags_str = (char *)(uintptr_t)(foff); for (ant_offset_t i = 0; i < flen; i++) { if (flags_str[i] == 'g') global_flag = true; if (flags_str[i] == 'y') sticky_flag = true; @@ -599,7 +598,7 @@ static ant_value_t builtin_regexp_exec(ant_t *js, ant_value_t *args, int nargs) if (global_flag || sticky_flag) { ant_offset_t lastindex_off = lkp(js, regexp, "lastIndex", 9); if (lastindex_off != 0) { - ant_value_t li_val = resolveprop(js, mkval(T_PROP, lastindex_off)); + ant_value_t li_val = js_propref_load(js, lastindex_off); if (vtype(li_val) == T_NUM) { double li = tod(li_val); if (li >= 0 && li <= (double)str_len) start_offset = (PCRE2_SIZE)li; @@ -653,7 +652,7 @@ static ant_value_t builtin_regexp_exec(ant_t *js, ant_value_t *args, int nargs) pcre2_pattern_info(compiled.code, PCRE2_INFO_NAMETABLE, (void *)&nametable); ant_value_t groups = js_mkobj(js); - js_set_proto(js, groups, js_mknull()); + js_set_proto_init(groups, js_mknull()); PCRE2_SPTR tabptr = nametable; for (uint32_t i = 0; i < namecount; i++) { @@ -701,9 +700,9 @@ static ant_value_t builtin_regexp_toString(ant_t *js, ant_value_t *args, int nar if (!buf) return js_mkerr(js, "oom"); size_t n = 0; buf[n++] = '/'; - memcpy(buf + n, &js->mem[src_off], src_len); n += src_len; + memcpy(buf + n, (const void *)(uintptr_t)src_off, src_len); n += src_len; buf[n++] = '/'; - memcpy(buf + n, &js->mem[fl_off], fl_len); n += fl_len; + memcpy(buf + n, (const void *)(uintptr_t)fl_off, fl_len); n += fl_len; ant_value_t result = js_mkstr(js, buf, n); free(buf); @@ -742,11 +741,11 @@ static ant_value_t builtin_regexp_compile(ant_t *js, ant_value_t *args, int narg js_setprop(js, rx, js_mkstr(js, "source", 6), pattern); ant_offset_t flen, foff = vstr(js, flags, &flen); - regexp_init_flags(js, rx, (const char *)&js->mem[foff], flen, false); + regexp_init_flags(js, rx, (const char *)(uintptr_t)(foff), flen, false); - ant_offset_t obj_off = (ant_offset_t)vdata(rx); + ant_object_t *rx_ptr = js_obj_ptr(rx); for (size_t i = 0; i < regex_cache_count; i++) { - if (regex_cache[i].obj_offset == obj_off) { + if (regex_cache[i].obj == rx_ptr) { pcre2_match_data_free(regex_cache[i].match_data); pcre2_code_free(regex_cache[i].code); regex_cache[i] = regex_cache[--regex_cache_count]; @@ -776,7 +775,7 @@ static ant_value_t builtin_regexp_escape(ant_t *js, ant_value_t *args, int nargs return js_mkerr_typed(js, JS_ERR_TYPE, "RegExp.escape requires a string argument"); ant_offset_t slen, soff = vstr(js, args[0], &slen); - const char *src = (const char *)&js->mem[soff]; + const char *src = (const char *)(uintptr_t)(soff); size_t buf_cap = slen * 6 + 1; char *buf = ant_calloc(buf_cap); @@ -927,7 +926,7 @@ ant_value_t builtin_regexp_symbol_match(ant_t *js, ant_value_t *args, int nargs) ant_offset_t str_len, str_off = vstr(js, str, &str_len); double advance = 1; if (full_unicode && li < (double)str_len) { - advance = (double)utf8_char_len_at((const char *)&js->mem[str_off], str_len, (ant_offset_t)li); + advance = (double)utf8_char_len_at((const char *)(uintptr_t)(str_off), str_len, (ant_offset_t)li); } js_setprop(js, rx, js_mkstr(js, "lastIndex", 9), tov(li + advance)); } } @@ -982,7 +981,7 @@ ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int narg ant_offset_t sl, so = vstr(js, str, &sl); double advance = 1; if (full_unicode && li < (double)sl) { - advance = (double)utf8_char_len_at((const char *)&js->mem[so], sl, (ant_offset_t)li); + advance = (double)utf8_char_len_at((const char *)(uintptr_t)(so), sl, (ant_offset_t)li); } js_setprop(js, rx, js_mkstr(js, "lastIndex", 9), tov(li + advance)); } @@ -1039,23 +1038,23 @@ ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int narg if (position >= next_src_pos) { str_off = vstr(js, str, &str_len); if (position > next_src_pos) - SB_APPEND((const char *)&js->mem[str_off + next_src_pos], position - next_src_pos); + SB_APPEND((const char *)(uintptr_t)(str_off + next_src_pos), position - next_src_pos); ant_offset_t rep_len, rep_off = vstr(js, rep_str, &rep_len); if (func_replace) { - SB_APPEND((const char *)&js->mem[rep_off], rep_len); + SB_APPEND((const char *)(uintptr_t)(rep_off), rep_len); } else { ant_offset_t ncap = js_arr_len(js, result); int num_caps = ncap > 1 ? (int)(ncap - 1) : 0; repl_capture_t caps_buf[16], *caps = num_caps <= 16 ? caps_buf : ant_calloc(sizeof(repl_capture_t) * (size_t)num_caps); for (int ci = 0; ci < num_caps; ci++) { ant_value_t cap = js_arr_get(js, result, (ant_offset_t)(ci + 1)); - if (vtype(cap) == T_STR) { ant_offset_t cl, co = vstr(js, cap, &cl); caps[ci] = (repl_capture_t){ (const char *)&js->mem[co], cl }; } + if (vtype(cap) == T_STR) { ant_offset_t cl, co = vstr(js, cap, &cl); caps[ci] = (repl_capture_t){ (const char *)(uintptr_t)(co), cl }; } else caps[ci] = (repl_capture_t){ NULL, 0 }; } ant_offset_t mlen, moff = vstr(js, matched, &mlen); str_off = vstr(js, str, &str_len); - repl_template((const char *)&js->mem[rep_off], rep_len, (const char *)&js->mem[moff], mlen, - (const char *)&js->mem[str_off], str_len, position, caps, num_caps, &buf, &buf_len, &buf_cap); + repl_template((const char *)(uintptr_t)(rep_off), rep_len, (const char *)(uintptr_t)(moff), mlen, + (const char *)(uintptr_t)(str_off), str_len, position, caps, num_caps, &buf, &buf_len, &buf_cap); if (caps != caps_buf) free(caps); } next_src_pos = position + matched_len; @@ -1064,7 +1063,7 @@ ant_value_t builtin_regexp_symbol_replace(ant_t *js, ant_value_t *args, int narg str_off = vstr(js, str, &str_len); if (next_src_pos < str_len) - SB_APPEND((const char *)&js->mem[str_off + next_src_pos], str_len - next_src_pos); + SB_APPEND((const char *)(uintptr_t)(str_off + next_src_pos), str_len - next_src_pos); #undef SB_APPEND @@ -1133,7 +1132,7 @@ ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) if (is_err(flags_str)) return flags_str; ant_offset_t flen, foff = vstr(js, flags_str, &flen); - const char *fptr = (const char *)&js->mem[foff]; + const char *fptr = (const char *)(uintptr_t)(foff); bool unicode_matching = false, has_sticky = false; for (ant_offset_t i = 0; i < flen; i++) { if (fptr[i] == 'u' || fptr[i] == 'v') unicode_matching = true; @@ -1145,7 +1144,7 @@ ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) char fbuf[16]; if (flen > 14) flen = 14; foff = vstr(js, flags_str, &flen); - fptr = (const char *)&js->mem[foff]; + fptr = (const char *)(uintptr_t)(foff); memcpy(fbuf, fptr, flen); fbuf[flen] = 'y'; new_flags = js_mkstr(js, fbuf, flen + 1); @@ -1187,7 +1186,7 @@ ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) if (vtype(z) == T_NULL) { if (unicode_matching) { str_off = vstr(js, str, &str_len); - q += utf8_char_len_at((const char *)&js->mem[str_off], str_len, q); + q += utf8_char_len_at((const char *)(uintptr_t)(str_off), str_len, q); } else q++; continue; } @@ -1200,13 +1199,13 @@ ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) if (e == p) { if (unicode_matching) { str_off = vstr(js, str, &str_len); - q += utf8_char_len_at((const char *)&js->mem[str_off], str_len, q); + q += utf8_char_len_at((const char *)(uintptr_t)(str_off), str_len, q); } else q++; continue; } str_off = vstr(js, str, NULL); - ant_value_t T_val = js_mkstr(js, (char *)&js->mem[str_off + p], q - p); + ant_value_t T_val = js_mkstr(js, (char *)(uintptr_t)(str_off + p), q - p); js_arr_push(js, A, T_val); lengthA++; if (lengthA == lim) return mkval(T_ARR, vdata(A)); @@ -1224,7 +1223,7 @@ ant_value_t builtin_regexp_symbol_split(ant_t *js, ant_value_t *args, int nargs) } str_off = vstr(js, str, &str_len); - ant_value_t trailing = js_mkstr(js, (char *)&js->mem[str_off + p], str_len - p); + ant_value_t trailing = js_mkstr(js, (char *)(uintptr_t)(str_off + p), str_len - p); js_arr_push(js, A, trailing); return mkval(T_ARR, vdata(A)); } @@ -1333,19 +1332,19 @@ static ant_value_t builtin_string_search(ant_t *js, ant_value_t *args, int nargs if (vtype(pattern) == T_OBJ) { ant_offset_t source_off = lkp(js, pattern, "source", 6); if (source_off == 0) return tov(-1); - ant_value_t source_val = resolveprop(js, mkval(T_PROP, source_off)); + ant_value_t source_val = js_propref_load(js, source_off); if (vtype(source_val) != T_STR) return tov(-1); ant_offset_t poff; poff = vstr(js, source_val, &pattern_len); - pattern_ptr = (char *)&js->mem[poff]; + pattern_ptr = (char *)(uintptr_t)(poff); ant_offset_t flags_off = lkp(js, pattern, "flags", 5); if (flags_off != 0) { - ant_value_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); + ant_value_t flags_val = js_propref_load(js, flags_off); if (vtype(flags_val) == T_STR) { ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *)&js->mem[foff]; + const char *flags_str = (char *)(uintptr_t)(foff); for (ant_offset_t i = 0; i < flen; i++) { if (flags_str[i] == 'i') ignore_case = true; if (flags_str[i] == 'm') multiline = true; @@ -1355,13 +1354,13 @@ static ant_value_t builtin_string_search(ant_t *js, ant_value_t *args, int nargs } else if (vtype(pattern) == T_STR) { ant_offset_t poff; poff = vstr(js, pattern, &pattern_len); - pattern_ptr = (char *)&js->mem[poff]; + pattern_ptr = (char *)(uintptr_t)(poff); } else { return tov(-1); } ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *)&js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); char pcre2_pattern[4096]; size_t pcre2_len = js_to_pcre2_pattern(pattern_ptr, pattern_len, pcre2_pattern, sizeof(pcre2_pattern)); @@ -1420,19 +1419,19 @@ static ant_value_t builtin_string_match(ant_t *js, ant_value_t *args, int nargs) ant_offset_t source_off = lkp(js, pattern, "source", 6); if (source_off == 0) return js_mknull(); - ant_value_t source_val = resolveprop(js, mkval(T_PROP, source_off)); + ant_value_t source_val = js_propref_load(js, source_off); if (vtype(source_val) != T_STR) return js_mknull(); ant_offset_t poff; poff = vstr(js, source_val, &pattern_len); - pattern_ptr = (char *)&js->mem[poff]; + pattern_ptr = (char *)(uintptr_t)(poff); ant_offset_t flags_off = lkp(js, pattern, "flags", 5); if (flags_off != 0) { - ant_value_t flags_val = resolveprop(js, mkval(T_PROP, flags_off)); + ant_value_t flags_val = js_propref_load(js, flags_off); if (vtype(flags_val) == T_STR) { ant_offset_t flen, foff = vstr(js, flags_val, &flen); - const char *flags_str = (char *)&js->mem[foff]; + const char *flags_str = (char *)(uintptr_t)(foff); for (ant_offset_t i = 0; i < flen; i++) { if (flags_str[i] == 'g') global_flag = true; if (flags_str[i] == 'i') ignore_case = true; @@ -1443,13 +1442,13 @@ static ant_value_t builtin_string_match(ant_t *js, ant_value_t *args, int nargs) } else if (vtype(pattern) == T_STR) { ant_offset_t poff; poff = vstr(js, pattern, &pattern_len); - pattern_ptr = (char *)&js->mem[poff]; + pattern_ptr = (char *)(uintptr_t)(poff); } else { return js_mknull(); } ant_offset_t str_len, str_off = vstr(js, str, &str_len); - const char *str_ptr = (char *)&js->mem[str_off]; + const char *str_ptr = (char *)(uintptr_t)(str_off); ant_value_t result = do_regex_match_pcre2( js, pattern_ptr, pattern_len, @@ -1469,7 +1468,7 @@ void init_regex_module(void) { ant_value_t object_proto = js->object; ant_value_t regexp_proto = js_mkobj(js); - js_set_proto(js, regexp_proto, object_proto); + js_set_proto_init(regexp_proto, object_proto); js_setprop(js, regexp_proto, js_mkstr(js, "test", 4), js_mkfun(builtin_regexp_test)); js_setprop(js, regexp_proto, js_mkstr(js, "exec", 4), js_mkfun(builtin_regexp_exec)); js_setprop(js, regexp_proto, js_mkstr(js, "toString", 8), js_mkfun(builtin_regexp_toString)); @@ -1491,7 +1490,7 @@ void init_regex_module(void) { js_setprop(js, regexp_proto, js_mkstr(js, "compile", 7), js_mkfun(builtin_regexp_compile)); ant_value_t regexp_ctor = js_mkobj(js); - js_set_slot(js, regexp_ctor, SLOT_CFUNC, js_mkfun(builtin_RegExp)); + js_set_slot(regexp_ctor, SLOT_CFUNC, js_mkfun(builtin_RegExp)); js_mkprop_fast(js, regexp_ctor, "prototype", 9, regexp_proto); js_mkprop_fast(js, regexp_ctor, "name", 4, js_mkstr(js, "RegExp", 6)); js_set_descriptor(js, regexp_ctor, "name", 4, 0); @@ -1519,24 +1518,18 @@ void init_regex_module(void) { js_setprop(js, string_proto, js_mkstr(js, "match", 5), js_mkfun(builtin_string_match)); } -void regex_gc_update_roots(ant_offset_t (*weak_off)(void *ctx, ant_offset_t old), GC_OP_VAL_ARGS) { - size_t write_idx = 0; - +void gc_sweep_regex_cache(void) { + size_t write = 0; for (size_t i = 0; i < regex_cache_count; i++) { - ant_offset_t old_off = regex_cache[i].obj_offset; - ant_offset_t new_off = weak_off(ctx, old_off); - - if (new_off == (ant_offset_t)~0) { + if (!gc_obj_is_marked(regex_cache[i].obj)) { pcre2_match_data_free(regex_cache[i].match_data); pcre2_code_free(regex_cache[i].code); - continue; + } else { + if (write != i) regex_cache[write] = regex_cache[i]; + write++; } - - regex_cache[i].obj_offset = new_off; - if (write_idx != i) regex_cache[write_idx] = regex_cache[i]; - write_idx++; } - regex_cache_count = write_idx; + regex_cache_count = write; } void cleanup_regex_module(void) { diff --git a/src/modules/server.c b/src/modules/server.c index 6a3567e..d2e2d74 100644 --- a/src/modules/server.c +++ b/src/modules/server.c @@ -6,16 +6,16 @@ #include #include -#include +#include #include #include "ant.h" -#include "gc.h" #include "reactor.h" #include "runtime.h" #include "internal.h" #include "silver/engine.h" +#include "gc/modules.h" #include "modules/server.h" #include "modules/json.h" @@ -349,7 +349,7 @@ static ant_value_t js_set_prop(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t store_obj = js_get_slot(js, fn, SLOT_DATA); + ant_value_t store_obj = js_get_slot(fn, SLOT_DATA); if (vtype(store_obj) == T_UNDEF) return js_mkundef(); if (vtype(args[0]) == T_STR) { @@ -365,7 +365,7 @@ static ant_value_t js_get_prop(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t store_obj = js_get_slot(js, fn, SLOT_DATA); + ant_value_t store_obj = js_get_slot(fn, SLOT_DATA); if (vtype(store_obj) == T_UNDEF) return js_mkundef(); if (vtype(args[0]) == T_STR) { @@ -381,7 +381,7 @@ static ant_value_t req_header(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t headers_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t headers_val = js_get_slot(fn, SLOT_DATA); if (vtype(headers_val) != T_NUM) return js_mkundef(); UT_array *headers = (UT_array *)(unsigned long)js_getnum(headers_val); @@ -405,7 +405,7 @@ static ant_value_t res_header(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -425,7 +425,7 @@ static ant_value_t res_status(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -442,7 +442,7 @@ static ant_value_t res_body(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -471,7 +471,7 @@ static ant_value_t res_html(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -497,7 +497,7 @@ static ant_value_t res_json(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -529,7 +529,7 @@ static ant_value_t res_notFound(ant_t *js, ant_value_t *args, int nargs) { (void)args; (void)nargs; ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -548,7 +548,7 @@ static ant_value_t res_redirect(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx_val = js_get_slot(fn, SLOT_DATA); if (vtype(ctx_val) != T_NUM) return js_mkundef(); response_ctx_t *ctx = (response_ctx_t *)(unsigned long)js_getnum(ctx_val); @@ -573,17 +573,17 @@ static ant_value_t res_redirect(ant_t *js, ant_value_t *args, int nargs) { } static char* gzip_compress(const char *data, size_t data_len, size_t *compressed_len) { - zng_stream stream; + z_stream stream; memset(&stream, 0, sizeof(stream)); - if (zng_deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) { + if (deflateInit2(&stream, Z_DEFAULT_COMPRESSION, Z_DEFLATED, 15 + 16, 8, Z_DEFAULT_STRATEGY) != Z_OK) { return NULL; } - size_t bound = zng_deflateBound(&stream, data_len); + size_t bound = deflateBound(&stream, data_len); char *compressed = malloc(bound); if (!compressed) { - zng_deflateEnd(&stream); + deflateEnd(&stream); return NULL; } @@ -592,14 +592,14 @@ static char* gzip_compress(const char *data, size_t data_len, size_t *compressed stream.next_out = (Bytef *)compressed; stream.avail_out = (uInt)bound; - if (zng_deflate(&stream, Z_FINISH) != Z_STREAM_END) { + if (deflate(&stream, Z_FINISH) != Z_STREAM_END) { free(compressed); - zng_deflateEnd(&stream); + deflateEnd(&stream); return NULL; } *compressed_len = stream.total_out; - zng_deflateEnd(&stream); + deflateEnd(&stream); return compressed; } @@ -1004,7 +1004,6 @@ ant_value_t js_serve(ant_t *js, ant_value_t *args, int nargs) { } server->pending_responses = NULL; - js_gc_throttle(true); js_reactor_set_poll_hook( (reactor_poll_hook_t) @@ -1014,7 +1013,6 @@ ant_value_t js_serve(ant_t *js, ant_value_t *args, int nargs) { js_run_event_loop(js); js_reactor_set_poll_hook(NULL, NULL); - js_gc_throttle(false); free(server); g_server = NULL; @@ -1022,14 +1020,15 @@ ant_value_t js_serve(ant_t *js, ant_value_t *args, int nargs) { return js_mknum(1); } +void gc_mark_server(ant_t *js, gc_mark_fn mark) { + if (g_server) { + mark(js, g_server->handler); + mark(js, g_server->store_obj); + mark(js, g_server->server_obj); + } +} + void init_server_module() { js_set(rt->js, rt->ant_obj, "serve", js_mkfun(js_serve)); } -void server_gc_update_roots(GC_OP_VAL_ARGS) { - if (g_server) { - op_val(ctx, &g_server->handler); - op_val(ctx, &g_server->store_obj); - op_val(ctx, &g_server->server_obj); - } -} diff --git a/src/modules/sessionstorage.c b/src/modules/sessionstorage.c index 5f52a01..ad00220 100644 --- a/src/modules/sessionstorage.c +++ b/src/modules/sessionstorage.c @@ -4,7 +4,6 @@ #include "ant.h" #include "errors.h" -#include "arena.h" #include "runtime.h" #include "internal.h" #include "descriptors.h" diff --git a/src/modules/shell.c b/src/modules/shell.c index 103048e..ed05fc1 100644 --- a/src/modules/shell.c +++ b/src/modules/shell.c @@ -80,7 +80,7 @@ static ant_value_t builtin_shell_text(ant_t *js, ant_value_t *args, int nargs) { (void)nargs; ant_value_t fn = js_getcurrentfunc(js); - return js_get_slot(js, fn, SLOT_DATA); + return js_get_slot(fn, SLOT_DATA); } static ant_value_t builtin_shell_lines(ant_t *js, ant_value_t *args, int nargs) { @@ -88,7 +88,7 @@ static ant_value_t builtin_shell_lines(ant_t *js, ant_value_t *args, int nargs) (void)nargs; ant_value_t fn = js_getcurrentfunc(js); - ant_value_t stdout_val = js_get_slot(js, fn, SLOT_DATA); + ant_value_t stdout_val = js_get_slot(fn, SLOT_DATA); size_t text_len; char *text = js_getstr(js, stdout_val, &text_len); diff --git a/src/modules/symbol.c b/src/modules/symbol.c index fdf64cb..ee353ce 100644 --- a/src/modules/symbol.c +++ b/src/modules/symbol.c @@ -10,10 +10,8 @@ #include "silver/engine.h" #include "modules/symbol.h" #include "descriptors.h" - -static ant_value_t g_iterator_proto_obj = {0}; -static ant_value_t g_array_iterator_proto_obj = {0}; -static ant_value_t g_string_iterator_proto_obj = {0}; +#include "gc/roots.h" +#include "gc/modules.h" #define DECL_SYM(name, _desc) static ant_value_t g_##name = {0}; WELLKNOWN_SYMBOLS(DECL_SYM) @@ -24,6 +22,9 @@ WELLKNOWN_SYMBOLS(DEF_GET_SYM) #undef DEF_GET_SYM static ant_value_t builtin_Symbol(ant_t *js, ant_value_t *args, int nargs) { + if (vtype(js->new_target) != T_UNDEF) + return js_mkerr_typed(js, JS_ERR_TYPE, "Symbol is not a constructor"); + const char *desc = NULL; if (nargs > 0 && vtype(args[0]) == T_STR) { desc = js_getstr(js, args[0], NULL); @@ -92,24 +93,24 @@ static ant_value_t iterator_next(ant_t *js, ant_value_t *args, int nargs) { } static ant_value_t get_iterator_prototype(ant_t *js) { - if (vtype(g_iterator_proto_obj) == T_OBJ) return g_iterator_proto_obj; + if (vtype(js->sym.iterator_proto) == T_OBJ) return js->sym.iterator_proto; - g_iterator_proto_obj = js_mkobj(js); - js_set_proto(js, g_iterator_proto_obj, js->object); - js_set_sym(js, g_iterator_proto_obj, g_iterator, js_mkfun(sym_this_cb)); + js->sym.iterator_proto = js_mkobj(js); + js_set_proto_init(js->sym.iterator_proto, js->object); + js_set_sym(js, js->sym.iterator_proto, g_iterator, js_mkfun(sym_this_cb)); - return g_iterator_proto_obj; + return js->sym.iterator_proto; } static ant_value_t get_array_iterator_prototype(ant_t *js) { - if (vtype(g_array_iterator_proto_obj) == T_OBJ) return g_array_iterator_proto_obj; + if (vtype(js->sym.array_iterator_proto) == T_OBJ) return js->sym.array_iterator_proto; ant_value_t iterator_proto = get_iterator_prototype(js); - g_array_iterator_proto_obj = js_mkobj(js); - js_set(js, g_array_iterator_proto_obj, "next", js_mkfun(iterator_next)); - js_set_proto(js, g_array_iterator_proto_obj, iterator_proto); + js->sym.array_iterator_proto = js_mkobj(js); + js_set(js, js->sym.array_iterator_proto, "next", js_mkfun(iterator_next)); + js_set_proto_init(js->sym.array_iterator_proto, iterator_proto); - return g_array_iterator_proto_obj; + return js->sym.array_iterator_proto; } static ant_value_t array_iterator(ant_t *js, ant_value_t *args, int nargs) { @@ -121,7 +122,7 @@ static ant_value_t array_iterator(ant_t *js, ant_value_t *args, int nargs) { js_set(js, iter, "__arr", arr); js_set(js, iter, "__idx", js_mknum(0)); js_set(js, iter, "__len", js_mknum(len)); - js_set_proto(js, iter, get_array_iterator_prototype(js)); + js_set_proto_init(iter, get_array_iterator_prototype(js)); return iter; } @@ -158,14 +159,14 @@ static ant_value_t string_iterator_next(ant_t *js, ant_value_t *args, int nargs) } static ant_value_t get_string_iterator_prototype(ant_t *js) { - if (vtype(g_string_iterator_proto_obj) == T_OBJ) return g_string_iterator_proto_obj; + if (vtype(js->sym.string_iterator_proto) == T_OBJ) return js->sym.string_iterator_proto; ant_value_t iterator_proto = get_iterator_prototype(js); - g_string_iterator_proto_obj = js_mkobj(js); - js_set(js, g_string_iterator_proto_obj, "next", js_mkfun(string_iterator_next)); - js_set_proto(js, g_string_iterator_proto_obj, iterator_proto); + js->sym.string_iterator_proto = js_mkobj(js); + js_set(js, js->sym.string_iterator_proto, "next", js_mkfun(string_iterator_next)); + js_set_proto_init(js->sym.string_iterator_proto, iterator_proto); - return g_string_iterator_proto_obj; + return js->sym.string_iterator_proto; } static ant_value_t string_iterator(ant_t *js, ant_value_t *args, int nargs) { @@ -176,7 +177,7 @@ static ant_value_t string_iterator(ant_t *js, ant_value_t *args, int nargs) { js_set(js, iter, "__str", str); js_set(js, iter, "__idx", js_mknum(0)); js_set(js, iter, "__len", js_mknum((double)len)); - js_set_proto(js, iter, get_string_iterator_prototype(js)); + js_set_proto_init(iter, get_string_iterator_prototype(js)); return iter; } @@ -204,18 +205,22 @@ ant_value_t maybe_call_symbol_method( } void js_define_species_getter(ant_t *js, ant_value_t ctor) { - if (vtype(ctor) == T_FUNC) ctor = js_func_obj(ctor); - if (vtype(ctor) != T_OBJ || vtype(g_species) != T_SYMBOL) return; + if (!is_object_type(ctor) || vtype(g_species) != T_SYMBOL) return; + ctor = js_as_obj(ctor); js_set_sym_getter_desc(js, ctor, g_species, js_mkfun(sym_this_cb), JS_DESC_C); } void init_symbol_module(void) { ant_t *js = rt->js; - g_iterator_proto_obj = js_mkundef(); - g_array_iterator_proto_obj = js_mkundef(); - g_string_iterator_proto_obj = js_mkundef(); + js->sym.iterator_proto = js_mkundef(); + js->sym.array_iterator_proto = js_mkundef(); + js->sym.string_iterator_proto = js_mkundef(); + gc_register_root(&js->sym.iterator_proto); + gc_register_root(&js->sym.array_iterator_proto); + gc_register_root(&js->sym.string_iterator_proto); + #define INIT_SYM(name, desc) g_##name = js_mksym(js, desc); WELLKNOWN_SYMBOLS(INIT_SYM) #undef INIT_SYM @@ -224,7 +229,7 @@ void init_symbol_module(void) { js_set(js, symbol_proto, "toString", js_mkfun(builtin_Symbol_toString)); ant_value_t symbol_ctor = js_mkobj(js); - js_set_slot(js, symbol_ctor, SLOT_CFUNC, js_mkfun(builtin_Symbol)); + js_set_slot(symbol_ctor, SLOT_CFUNC, js_mkfun(builtin_Symbol)); js_setprop(js, symbol_ctor, js_mkstr(js, "for", 3), js_mkfun(builtin_Symbol_for)); js_set(js, symbol_ctor, "keyFor", js_mkfun(builtin_Symbol_keyFor)); js_set(js, symbol_ctor, "prototype", symbol_proto); @@ -273,14 +278,13 @@ void init_symbol_module(void) { js_define_species_getter(js, array_ctor); } -void symbol_gc_update_roots(GC_OP_VAL_ARGS) { - op_val(ctx, &g_iterator_proto_obj); - op_val(ctx, &g_array_iterator_proto_obj); - op_val(ctx, &g_string_iterator_proto_obj); +void gc_mark_symbols(ant_t *js, gc_mark_fn mark) { + mark(js, js->sym.iterator_proto); + mark(js, js->sym.array_iterator_proto); + mark(js, js->sym.string_iterator_proto); - #define GC_SYM(name, _desc) op_val(ctx, &g_##name); + #define GC_SYM(name, _desc) mark(js, g_##name); WELLKNOWN_SYMBOLS(GC_SYM) #undef GC_SYM - - sym_gc_update_all(op_val, ctx); } + diff --git a/src/modules/textcodec.c b/src/modules/textcodec.c index a720eab..1bfe02e 100644 --- a/src/modules/textcodec.c +++ b/src/modules/textcodec.c @@ -38,7 +38,7 @@ static ant_value_t js_textencoder_encode(ant_t *js, ant_value_t *args, int nargs if (vtype(arr) == T_ERR) return arr; if (str_len > 0) { - ant_value_t ta_data_val = js_get_slot(js, arr, SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(arr, SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (ta_data && ta_data->buffer && ta_data->buffer->data) memcpy(ta_data->buffer->data, str, str_len); } @@ -63,7 +63,7 @@ static ant_value_t js_textencoder_encodeInto(ant_t *js, ant_value_t *args, int n } } - ant_value_t ta_data_val = js_get_slot(js, args[1], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[1], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (!ta_data) return js_mkerr(js, "Second argument must be a Uint8Array"); @@ -100,7 +100,7 @@ static ant_value_t js_textdecoder_decode(ant_t *js, ant_value_t *args, int nargs return js_mkstr(js, "", 0); } - ant_value_t ta_data_val = js_get_slot(js, args[0], SLOT_BUFFER); + ant_value_t ta_data_val = js_get_slot(args[0], SLOT_BUFFER); TypedArrayData *ta_data = (TypedArrayData *)js_gettypedarray(ta_data_val); if (ta_data) { if (!ta_data->buffer) return js_mkstr(js, "", 0); @@ -109,7 +109,7 @@ static ant_value_t js_textdecoder_decode(ant_t *js, ant_value_t *args, int nargs return js_mkstr(js, (const char *)data, len); } - ant_value_t ab_data_val = js_get_slot(js, args[0], SLOT_BUFFER); + ant_value_t ab_data_val = js_get_slot(args[0], SLOT_BUFFER); if (vtype(ab_data_val) == T_NUM) { ArrayBufferData *ab_data = (ArrayBufferData *)(uintptr_t)js_getnum(ab_data_val); if (!ab_data || !ab_data->data) return js_mkstr(js, "", 0); @@ -145,22 +145,26 @@ void init_textcodec_module(void) { ant_t *js = rt->js; ant_value_t glob = js_glob(js); - ant_value_t textencoder_constructor = js_mkfun(js_textencoder_constructor); + ant_value_t textencoder_ctor_obj = js_mkobj(js); + js_set_slot(textencoder_ctor_obj, SLOT_CFUNC, js_mkfun(js_textencoder_constructor)); ant_value_t textencoder_proto = js_mkobj(js); js_set(js, textencoder_proto, "encode", js_mkfun(js_textencoder_encode)); js_set(js, textencoder_proto, "encodeInto", js_mkfun(js_textencoder_encodeInto)); js_set(js, textencoder_proto, "encoding", js_mkstr(js, "utf-8", 5)); - js_set(js, textencoder_constructor, "prototype", textencoder_proto); + js_set(js, textencoder_ctor_obj, "prototype", textencoder_proto); + ant_value_t textencoder_constructor = js_obj_to_func(textencoder_ctor_obj); js_set(js, glob, "TextEncoder", textencoder_constructor); - ant_value_t textdecoder_constructor = js_mkfun(js_textdecoder_constructor); + ant_value_t textdecoder_ctor_obj = js_mkobj(js); + js_set_slot(textdecoder_ctor_obj, SLOT_CFUNC, js_mkfun(js_textdecoder_constructor)); ant_value_t textdecoder_proto = js_mkobj(js); js_set(js, textdecoder_proto, "decode", js_mkfun(js_textdecoder_decode)); js_set(js, textdecoder_proto, "encoding", js_mkstr(js, "utf-8", 5)); js_set(js, textdecoder_proto, "fatal", js_false); js_set(js, textdecoder_proto, "ignoreBOM", js_false); - js_set(js, textdecoder_constructor, "prototype", textdecoder_proto); + js_set(js, textdecoder_ctor_obj, "prototype", textdecoder_proto); + ant_value_t textdecoder_constructor = js_obj_to_func(textdecoder_ctor_obj); js_set(js, glob, "TextDecoder", textdecoder_constructor); } diff --git a/src/modules/timer.c b/src/modules/timer.c index dbf4ed4..246fa9f 100644 --- a/src/modules/timer.c +++ b/src/modules/timer.c @@ -6,11 +6,14 @@ #include #include -#include "arena.h" #include "errors.h" -#include "silver/engine.h" #include "runtime.h" + +#include "silver/engine.h" +#include "gc/roots.h" +#include "gc/modules.h" #include "modules/timer.h" +#include "modules/symbol.h" typedef struct timer_entry { uv_timer_t handle; @@ -27,7 +30,7 @@ typedef struct timer_entry { typedef struct microtask_entry { ant_value_t callback; - uint32_t promise_id; + ant_value_t promise; struct microtask_entry *next; } microtask_entry_t; @@ -82,6 +85,29 @@ static int timer_copy_args(timer_entry_t *entry, ant_value_t *args, int nargs) { return 0; } +static ant_value_t timer_to_primitive(ant_t *js, ant_value_t *args, int nargs) { + return js_get(js, js_getthis(js), "id"); +} + +static ant_value_t timer_make_object(ant_t *js, int id, double delay_ms, int is_interval, ant_value_t callback) { + ant_value_t obj = js_mkobj(js); + + js_set(js, obj, "id", js_mknum((double)id)); + js_set(js, obj, "delay", js_mknum(delay_ms)); + js_set(js, obj, "repeat", is_interval ? js_mknum(delay_ms) : js_mknull()); + js_set(js, obj, "callback", callback); + + js_set_sym(js, obj, get_toStringTag_sym(), js_mkstr(js, is_interval ? "Interval" : "Timeout", is_interval ? 8 : 7)); + js_set_sym(js, obj, get_toPrimitive_sym(), js_mkfun(timer_to_primitive)); + + return obj; +} + +static int timer_id_from_arg(ant_t *js, ant_value_t arg) { + if (vtype(arg) == T_NUM) return (int)js_getnum(arg); + return (int)js_getnum(js_get(js, arg, "id")); +} + static void timer_close_cb(uv_handle_t *h) { timer_entry_t *entry = (timer_entry_t *)h->data; if (!entry) return; @@ -122,13 +148,13 @@ static void timer_callback(uv_timer_t *handle) { // setTimeout(callback, delay, ...args) static ant_value_t js_set_timeout(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) { - return js_mkerr(js, "setTimeout requires 2 arguments (callback, delay)"); + if (nargs < 1) { + return js_mkerr(js, "setTimeout requires at least 1 argument (callback)"); } ant_value_t callback = args[0]; - double delay_ms = js_getnum(args[1]); - uint64_t ms = delay_ms < 1 ? 0 : (uint64_t)delay_ms; + double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; + uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 0; timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); @@ -149,19 +175,19 @@ static ant_value_t js_set_timeout(ant_t *js, ant_value_t *args, int nargs) { add_timer_entry(entry); timer_state.active_timer_count++; uv_timer_start(&entry->handle, timer_callback, ms, 0); - - return js_mknum((double)entry->timer_id); + + return timer_make_object(js, entry->timer_id, delay_ms, 0, callback); } // setInterval(callback, delay, ...args) static ant_value_t js_set_interval(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 2) { - return js_mkerr(js, "setInterval requires 2 arguments (callback, delay)"); + if (nargs < 1) { + return js_mkerr(js, "setInterval requires at least 1 argument (callback)"); } ant_value_t callback = args[0]; - double delay_ms = js_getnum(args[1]); - uint64_t ms = delay_ms < 1 ? 1 : (uint64_t)delay_ms; + double delay_ms = nargs > 1 ? js_getnum(args[1]) : 0; + uint64_t ms = delay_ms >= 1 ? (uint64_t)delay_ms : 1; timer_entry_t *entry = ant_calloc(sizeof(timer_entry_t)); if (entry == NULL) return js_mkerr(js, "failed to allocate timer"); @@ -182,23 +208,21 @@ static ant_value_t js_set_interval(ant_t *js, ant_value_t *args, int nargs) { add_timer_entry(entry); timer_state.active_timer_count++; uv_timer_start(&entry->handle, timer_callback, ms, ms); - - return js_mknum((double)entry->timer_id); + + return timer_make_object(js, entry->timer_id, delay_ms, 1, callback); } -// clearTimeout(timerId) +// clearTimeout(timerId | timerObject) static ant_value_t js_clear_timeout(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 1) return js_mkundef(); - int timer_id = (int)js_getnum(args[0]); + int timer_id = timer_id_from_arg(js, args[0]); for (timer_entry_t *entry = timer_state.timers; entry != NULL; entry = entry->next) { - if (entry->timer_id == timer_id && entry->active) { - entry->active = 0; timer_state.active_timer_count--; - if (!uv_is_closing((uv_handle_t *)&entry->handle)) { - uv_close((uv_handle_t *)&entry->handle, timer_close_cb); - } break; - } - } + if (entry->timer_id == timer_id && entry->active) { + entry->active = 0; timer_state.active_timer_count--; + if (!uv_is_closing((uv_handle_t *)&entry->handle)) uv_close((uv_handle_t *)&entry->handle, timer_close_cb); + break; + }} return js_mkundef(); } @@ -228,14 +252,18 @@ static ant_value_t js_set_immediate(ant_t *js, ant_value_t *args, int nargs) { timer_state.immediates_tail->next = entry; timer_state.immediates_tail = entry; } + + ant_value_t obj = js_mkobj(js); + js_set(js, obj, "id", js_mknum((double)entry->immediate_id)); + js_set(js, obj, "callback", callback); - return js_mknum((double)entry->immediate_id); + return obj; } -// clearImmediate(immediateId) +// clearImmediate(immediateId | immediateObject) static ant_value_t js_clear_immediate(ant_t *js, ant_value_t *args, int nargs) { - if (nargs < 1) return js_mkundef(); - int immediate_id = (int)js_getnum(args[0]); + if (nargs < 1) return js_mkundef(); + int immediate_id = timer_id_from_arg(js, args[0]); for (immediate_entry_t *entry = timer_state.immediates; entry != NULL; entry = entry->next) { if (entry->immediate_id == immediate_id) { entry->active = 0; break; } @@ -259,7 +287,7 @@ void queue_microtask(ant_t *js, ant_value_t callback) { if (entry == NULL) return; entry->callback = callback; - entry->promise_id = 0; + entry->promise = js_mkundef(); entry->next = NULL; if (timer_state.microtasks_tail == NULL) { @@ -271,12 +299,12 @@ void queue_microtask(ant_t *js, ant_value_t callback) { } } -void queue_promise_trigger(uint32_t promise_id) { +void queue_promise_trigger(ant_value_t promise) { microtask_entry_t *entry = ant_calloc(sizeof(microtask_entry_t)); if (entry == NULL) return; - entry->callback = 0; - entry->promise_id = promise_id; + entry->callback = js_mkundef(); + entry->promise = promise; entry->next = NULL; if (timer_state.microtasks_tail == NULL) { @@ -297,11 +325,19 @@ void process_microtasks(ant_t *js) { timer_state.microtasks_tail = NULL; } - if (entry->promise_id != 0) { - js_process_promise_handlers(js, entry->promise_id); + if (vtype(entry->promise) == T_PROMISE) { + GC_ROOT_SAVE(root_mark, js); + ant_value_t promise = entry->promise; + GC_ROOT_PIN(js, promise); + js_process_promise_handlers(js, promise); + GC_ROOT_RESTORE(js, root_mark); } else { + GC_ROOT_SAVE(root_mark, js); + ant_value_t callback = entry->callback; + GC_ROOT_PIN(js, callback); ant_value_t args[0]; - sv_vm_call(js->vm, js, entry->callback, js_mkundef(), args, 0, NULL, false); + sv_vm_call(js->vm, js, callback, js_mkundef(), args, 0, NULL, false); + GC_ROOT_RESTORE(js, root_mark); } free(entry); @@ -360,15 +396,16 @@ void init_timer_module() { js_set(js, global, "queueMicrotask", js_mkfun(js_queue_microtask)); } -void timer_gc_update_roots(GC_OP_VAL_ARGS) { +void gc_mark_timers(ant_t *js, gc_mark_fn mark) { for (timer_entry_t *t = timer_state.timers; t; t = t->next) { - op_val(ctx, &t->callback); - for (int i = 0; i < t->nargs; i++) op_val(ctx, &t->args[i]); + mark(js, t->callback); + for (int i = 0; i < t->nargs; i++) mark(js, t->args[i]); } for (microtask_entry_t *m = timer_state.microtasks; m; m = m->next) { - if (m->promise_id == 0) op_val(ctx, &m->callback); + mark(js, m->callback); + mark(js, m->promise); } for (immediate_entry_t *i = timer_state.immediates; i; i = i->next) { - op_val(ctx, &i->callback); + mark(js, i->callback); } } diff --git a/src/modules/tty.c b/src/modules/tty.c index 9050e2c..49fcbcc 100644 --- a/src/modules/tty.c +++ b/src/modules/tty.c @@ -598,7 +598,7 @@ static ant_value_t tty_read_stream_constructor(ant_t *js, ant_value_t *args, int ant_value_t ctor = js_getcurrentfunc(js); ant_value_t proto = js_get(js, ctor, "prototype"); ant_value_t obj = js_mkobj(js); - if (is_special_object(proto)) js_set_proto(js, obj, proto); + if (is_special_object(proto)) js_set_proto_init(obj, proto); ensure_stream_common_props(js, obj, fd); js_set(js, obj, "isRaw", js_false); @@ -627,7 +627,7 @@ static ant_value_t tty_write_stream_constructor(ant_t *js, ant_value_t *args, in ant_value_t ctor = js_getcurrentfunc(js); ant_value_t proto = js_get(js, ctor, "prototype"); ant_value_t obj = js_mkobj(js); - if (is_special_object(proto)) js_set_proto(js, obj, proto); + if (is_special_object(proto)) js_set_proto_init(obj, proto); ensure_stream_common_props(js, obj, fd); return obj; @@ -701,17 +701,21 @@ void init_tty_module(void) { ant_value_t tty_library(ant_t *js) { ant_value_t lib = js_mkobj(js); - ant_value_t read_ctor = js_mkfun(tty_read_stream_constructor); + ant_value_t read_ctor_obj = js_mkobj(js); + js_set_slot(read_ctor_obj, SLOT_CFUNC, js_mkfun(tty_read_stream_constructor)); ant_value_t read_proto = js_mkobj(js); setup_readstream_proto(js, read_proto); + js_set(js, read_ctor_obj, "prototype", read_proto); + ant_value_t read_ctor = js_obj_to_func(read_ctor_obj); js_set(js, read_proto, "constructor", read_ctor); - js_set(js, read_ctor, "prototype", read_proto); - ant_value_t write_ctor = js_mkfun(tty_write_stream_constructor); + ant_value_t write_ctor_obj = js_mkobj(js); + js_set_slot(write_ctor_obj, SLOT_CFUNC, js_mkfun(tty_write_stream_constructor)); ant_value_t write_proto = js_mkobj(js); setup_writestream_proto(js, write_proto); + js_set(js, write_ctor_obj, "prototype", write_proto); + ant_value_t write_ctor = js_obj_to_func(write_ctor_obj); js_set(js, write_proto, "constructor", write_ctor); - js_set(js, write_ctor, "prototype", write_proto); js_set(js, lib, "isatty", js_mkfun(tty_isatty)); js_set(js, lib, "ReadStream", read_ctor); diff --git a/src/modules/url.c b/src/modules/url.c index eeabdde..c31a3a0 100644 --- a/src/modules/url.c +++ b/src/modules/url.c @@ -305,10 +305,10 @@ static ant_value_t url_toString(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t js_URLSearchParams(ant_t *js, ant_value_t *args, int nargs) { ant_value_t usp = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "URLSearchParams", 15); - if (is_special_object(proto)) js_set_proto(js, usp, proto); + if (is_special_object(proto)) js_set_proto_init(usp, proto); ant_value_t entries = js_mkarr(js); - js_set_slot(js, usp, SLOT_ENTRIES, entries); + js_set_slot(usp, SLOT_ENTRIES, entries); if (nargs < 1 || vtype(args[0]) != T_STR) return usp; char *init = js_getstr(js, args[0], NULL); @@ -350,7 +350,7 @@ static ant_value_t js_URL(ant_t *js, ant_value_t *args, int nargs) { ant_value_t url_obj = js_mkobj(js); ant_value_t proto = js_get_ctor_proto(js, "URL", 3); - if (is_special_object(proto)) js_set_proto(js, url_obj, proto); + if (is_special_object(proto)) js_set_proto_init(url_obj, proto); js_set(js, url_obj, "protocol", js_mkstr(js, parsed.protocol, strlen(parsed.protocol))); js_set(js, url_obj, "username", js_mkstr(js, parsed.username, strlen(parsed.username))); @@ -365,15 +365,15 @@ static ant_value_t js_URL(ant_t *js, ant_value_t *args, int nargs) { ant_value_t search_params = js_mkobj(js); ant_value_t usp_proto = js_get_ctor_proto(js, "URLSearchParams", 15); - if (is_special_object(usp_proto)) js_set_proto(js, search_params, usp_proto); - js_set_slot(js, search_params, SLOT_DATA, url_obj); + if (is_special_object(usp_proto)) js_set_proto_init(search_params, usp_proto); + js_set_slot(search_params, SLOT_DATA, url_obj); if (parsed.search && *parsed.search) { const char *qs = parsed.search[0] == '?' ? parsed.search + 1 : parsed.search; ant_value_t arg = js_mkstr(js, qs, strlen(qs)); ant_value_t tmp = js_URLSearchParams(js, &arg, 1); - js_set_slot(js, search_params, SLOT_ENTRIES, js_get_slot(js, tmp, SLOT_ENTRIES)); + js_set_slot(search_params, SLOT_ENTRIES, js_get_slot(tmp, SLOT_ENTRIES)); } else { - js_set_slot(js, search_params, SLOT_ENTRIES, js_mkarr(js)); + js_set_slot(search_params, SLOT_ENTRIES, js_mkarr(js)); } js_set(js, url_obj, "searchParams", search_params); @@ -388,7 +388,7 @@ static ant_value_t usp_get(ant_t *js, ant_value_t *args, int nargs) { char *key = js_getstr(js, args[0], NULL); if (!key) return js_mknull(); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_mknull(); ant_offset_t len = js_arr_len(js, entries); @@ -409,7 +409,7 @@ static ant_value_t usp_getAll(ant_t *js, ant_value_t *args, int nargs) { if (!key) return js_mkarr(js); ant_value_t result = js_mkarr(js); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return result; ant_offset_t len = js_arr_len(js, entries); @@ -429,7 +429,7 @@ static ant_value_t usp_has(ant_t *js, ant_value_t *args, int nargs) { char *key = js_getstr(js, args[0], NULL); if (!key) return js_false; - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); if (!is_special_object(entries)) return js_false; ant_offset_t len = js_arr_len(js, entries); @@ -443,10 +443,10 @@ static ant_value_t usp_has(ant_t *js, ant_value_t *args, int nargs) { } static void usp_sync_url(ant_t *js, ant_value_t this_val) { - ant_value_t url_obj = js_get_slot(js, this_val, SLOT_DATA); + ant_value_t url_obj = js_get_slot(this_val, SLOT_DATA); if (!is_special_object(url_obj)) return; - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); ant_offset_t len = js_arr_len(js, entries); size_t buf_size = 1024; @@ -487,7 +487,7 @@ static ant_value_t usp_set(ant_t *js, ant_value_t *args, int nargs) { char *key = js_getstr(js, args[0], NULL); if (!key) return js_mkundef(); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); ant_offset_t len = js_arr_len(js, entries); ant_value_t new_entries = js_mkarr(js); @@ -514,7 +514,7 @@ static ant_value_t usp_set(ant_t *js, ant_value_t *args, int nargs) { js_arr_push(js, new_entries, new_entry); } - js_set_slot(js, this_val, SLOT_ENTRIES, new_entries); + js_set_slot_wb(js, this_val, SLOT_ENTRIES, new_entries); usp_sync_url(js, this_val); return js_mkundef(); @@ -524,7 +524,7 @@ static ant_value_t usp_append(ant_t *js, ant_value_t *args, int nargs) { if (nargs < 2) return js_mkundef(); ant_value_t this_val = js_getthis(js); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); ant_value_t entry = js_mkarr(js); js_arr_push(js, entry, args[0]); js_arr_push(js, entry, args[1]); @@ -540,7 +540,7 @@ static ant_value_t usp_delete(ant_t *js, ant_value_t *args, int nargs) { char *key = js_getstr(js, args[0], NULL); if (!key) return js_mkundef(); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); ant_offset_t len = js_arr_len(js, entries); ant_value_t new_entries = js_mkarr(js); @@ -550,16 +550,15 @@ static ant_value_t usp_delete(ant_t *js, ant_value_t *args, int nargs) { if (!ks || strcmp(ks, key) != 0) js_arr_push(js, new_entries, entry); } - js_set_slot(js, this_val, SLOT_ENTRIES, new_entries); + js_set_slot_wb(js, this_val, SLOT_ENTRIES, new_entries); usp_sync_url(js, this_val); - js->needs_gc = true; return js_mkundef(); } static ant_value_t usp_toString(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_val = js_getthis(js); - ant_value_t entries = js_get_slot(js, this_val, SLOT_ENTRIES); + ant_value_t entries = js_get_slot(this_val, SLOT_ENTRIES); ant_offset_t len = js_arr_len(js, entries); size_t buf_size = 1024; @@ -605,7 +604,7 @@ void init_url_module(void) { js_set(js, url_proto, "toString", js_mkfun(url_toString)); js_set_sym(js, url_proto, get_toStringTag_sym(), js_mkstr(js, "URL", 3)); - js_set_slot(js, url_ctor, SLOT_CFUNC, js_mkfun(js_URL)); + js_set_slot(url_ctor, SLOT_CFUNC, js_mkfun(js_URL)); js_mkprop_fast(js, url_ctor, "prototype", 9, url_proto); js_mkprop_fast(js, url_ctor, "name", 4, ANT_STRING("URL")); js_set_descriptor(js, url_ctor, "name", 4, 0); @@ -624,7 +623,7 @@ void init_url_module(void) { js_set(js, usp_proto, "toString", js_mkfun(usp_toString)); js_set_sym(js, usp_proto, get_toStringTag_sym(), js_mkstr(js, "URLSearchParams", 15)); - js_set_slot(js, usp_ctor, SLOT_CFUNC, js_mkfun(js_URLSearchParams)); + js_set_slot(usp_ctor, SLOT_CFUNC, js_mkfun(js_URLSearchParams)); js_mkprop_fast(js, usp_ctor, "prototype", 9, usp_proto); js_mkprop_fast(js, usp_ctor, "name", 4, ANT_STRING("URLSearchParams")); js_set_descriptor(js, usp_ctor, "name", 4, 0); diff --git a/src/modules/util.c b/src/modules/util.c index 3724509..6d85aaa 100644 --- a/src/modules/util.c +++ b/src/modules/util.c @@ -315,14 +315,14 @@ static ant_value_t util_style_text(ant_t *js, ant_value_t *args, int nargs) { static ant_value_t util_promisify_callback(ant_t *js, ant_value_t *args, int nargs) { ant_value_t fn = js_getcurrentfunc(js); - ant_value_t ctx = js_get_slot(js, fn, SLOT_DATA); + ant_value_t ctx = js_get_slot(fn, SLOT_DATA); if (!is_object_type(ctx)) return js_mkundef(); - ant_value_t settled = js_get_slot(js, ctx, SLOT_SETTLED); + ant_value_t settled = js_get_slot(ctx, SLOT_SETTLED); if (vtype(settled) == T_BOOL && settled == js_true) return js_mkundef(); - js_set_slot(js, ctx, SLOT_SETTLED, js_true); + js_set_slot(ctx, SLOT_SETTLED, js_true); - ant_value_t promise = js_get_slot(js, ctx, SLOT_DATA); + ant_value_t promise = js_get_slot(ctx, SLOT_DATA); if (!is_object_type(promise)) return js_mkundef(); if (nargs > 0 && !is_null(args[0]) && !is_undefined(args[0])) { @@ -348,13 +348,13 @@ static ant_value_t util_promisify_callback(ant_t *js, ant_value_t *args, int nar static ant_value_t util_promisified_call(ant_t *js, ant_value_t *args, int nargs) { ant_value_t fn = js_getcurrentfunc(js); - ant_value_t original = js_get_slot(js, fn, SLOT_DATA); + ant_value_t original = js_get_slot(fn, SLOT_DATA); if (!util_is_callable(original)) return js_mkerr(js, "promisified target is not callable"); ant_value_t promise = js_mkpromise(js); ant_value_t ctx = js_mkobj(js); - js_set_slot(js, ctx, SLOT_DATA, promise); - js_set_slot(js, ctx, SLOT_SETTLED, js_false); + js_set_slot(ctx, SLOT_DATA, promise); + js_set_slot(ctx, SLOT_SETTLED, js_false); ant_value_t cb = js_heavy_mkfun(js, util_promisify_callback, ctx); ant_value_t *call_args = (ant_value_t *)malloc((size_t)(nargs + 1) * sizeof(ant_value_t)); @@ -371,14 +371,14 @@ static ant_value_t util_promisified_call(ant_t *js, ant_value_t *args, int nargs ); free(call_args); - ant_value_t settled = js_get_slot(js, ctx, SLOT_SETTLED); + ant_value_t settled = js_get_slot(ctx, SLOT_SETTLED); bool is_settled = (vtype(settled) == T_BOOL && settled == js_true); if (!is_settled && (is_err(call_result) || js->thrown_exists)) { ant_value_t ex = js->thrown_exists ? js->thrown_value : call_result; js->thrown_exists = false; js->thrown_value = js_mkundef(); js->thrown_stack = js_mkundef(); - js_set_slot(js, ctx, SLOT_SETTLED, js_true); + js_set_slot(ctx, SLOT_SETTLED, js_true); js_reject_promise(js, promise, ex); } diff --git a/src/modules/worker_threads.c b/src/modules/worker_threads.c index 7fb4d44..29abc8a 100644 --- a/src/modules/worker_threads.c +++ b/src/modules/worker_threads.c @@ -30,6 +30,7 @@ extern char **environ; #include "modules/json.h" #include "modules/symbol.h" #include "modules/worker_threads.h" +#include "gc/modules.h" #define WT_ENV_MODE "ANT_WORKER_THREADS_MODE" #define WT_ENV_DATA_JSON "ANT_WORKER_DATA_JSON" @@ -51,11 +52,15 @@ typedef struct ant_worker_thread { char *line_buf; size_t line_len; size_t line_cap; - ant_handle_t self_root; - ant_handle_t terminate_root; - bool has_terminate_root; + ant_value_t self_val; + ant_value_t terminate_val; + bool has_terminate_val; + struct ant_worker_thread *next; + struct ant_worker_thread *prev; } ant_worker_thread_t; +static ant_worker_thread_t *active_workers_head = NULL; + static inline bool wt_is_callable(ant_value_t v) { uint8_t t = vtype(v); return t == T_FUNC || t == T_CFUNC; @@ -67,10 +72,10 @@ static bool wt_is_worker_mode(void) { } static ant_value_t wt_get_or_create_env_store(ant_t *js) { - ant_value_t store = js_get_slot(js, js->global, SLOT_WT_ENV_STORE); + ant_value_t store = js_get_slot(js->global, SLOT_WT_ENV_STORE); if (!is_object_type(store)) { store = js_mkobj(js); - js_set_slot(js, js->global, SLOT_WT_ENV_STORE, store); + js_set_slot(js->global, SLOT_WT_ENV_STORE, store); } return store; } @@ -86,61 +91,61 @@ static void wt_init_env_store(ant_t *js, bool is_worker) { if (is_object_type(parsed)) store = parsed; } } - js_set_slot(js, js->global, SLOT_WT_ENV_STORE, store); + js_set_slot(js->global, SLOT_WT_ENV_STORE, store); } static ant_worker_thread_t *wt_get_worker(ant_t *js, ant_value_t this_obj) { if (!is_object_type(this_obj)) return NULL; - ant_value_t data = js_get_slot(js, this_obj, SLOT_DATA); + ant_value_t data = js_get_slot(this_obj, SLOT_DATA); if (vtype(data) != T_NUM) return NULL; return (ant_worker_thread_t *)(uintptr_t)js_getnum(data); } static bool wt_is_message_port(ant_t *js, ant_value_t obj) { if (!is_object_type(obj)) return false; - ant_value_t tag = js_get_slot(js, obj, SLOT_WT_PORT_TAG); + ant_value_t tag = js_get_slot(obj, SLOT_WT_PORT_TAG); return js_truthy(js, tag); } static ant_value_t wt_get_message_port_proto(ant_t *js) { - ant_value_t proto = js_get_slot(js, js->global, SLOT_WT_PORT_PROTO); + ant_value_t proto = js_get_slot(js->global, SLOT_WT_PORT_PROTO); if (!is_object_type(proto)) proto = js_mkobj(js); return proto; } static ant_value_t wt_make_message_port(ant_t *js) { ant_value_t port = js_mkobj(js); - js_set_proto(js, port, wt_get_message_port_proto(js)); - js_set_slot(js, port, SLOT_WT_PORT_TAG, js_true); - js_set_slot(js, port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum(0)); - js_set_slot(js, port, SLOT_WT_PORT_PEER, js_mknull()); - js_set_slot(js, port, SLOT_WT_PORT_CLOSED, js_false); - js_set_slot(js, port, SLOT_WT_PORT_STARTED, js_false); - js_set_slot(js, port, SLOT_WT_PORT_ON_MESSAGE, js_mkundef()); - js_set_slot(js, port, SLOT_WT_PORT_ONCE_MESSAGE, js_mkundef()); + js_set_proto_init(port, wt_get_message_port_proto(js)); + js_set_slot(port, SLOT_WT_PORT_TAG, js_true); + js_set_slot(port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum(0)); + js_set_slot(port, SLOT_WT_PORT_PEER, js_mknull()); + js_set_slot(port, SLOT_WT_PORT_CLOSED, js_false); + js_set_slot(port, SLOT_WT_PORT_STARTED, js_false); + js_set_slot(port, SLOT_WT_PORT_ON_MESSAGE, js_mkundef()); + js_set_slot(port, SLOT_WT_PORT_ONCE_MESSAGE, js_mkundef()); js_set(js, port, "onmessage", js_mkundef()); return port; } static bool wt_port_is_closed(ant_t *js, ant_value_t port) { - return js_truthy(js, js_get_slot(js, port, SLOT_WT_PORT_CLOSED)); + return js_truthy(js, js_get_slot(port, SLOT_WT_PORT_CLOSED)); } static void wt_port_set_closed(ant_t *js, ant_value_t port, bool closed) { - js_set_slot(js, port, SLOT_WT_PORT_CLOSED, js_bool(closed)); + js_set_slot(port, SLOT_WT_PORT_CLOSED, js_bool(closed)); } static bool wt_port_queue_dequeue(ant_t *js, ant_value_t port, ant_value_t *out) { - ant_value_t queue = js_get_slot(js, port, SLOT_WT_PORT_QUEUE); + ant_value_t queue = js_get_slot(port, SLOT_WT_PORT_QUEUE); if (vtype(queue) != T_ARR) return false; - ant_value_t head_val = js_get_slot(js, port, SLOT_WT_PORT_HEAD); + ant_value_t head_val = js_get_slot(port, SLOT_WT_PORT_HEAD); ant_offset_t head = (vtype(head_val) == T_NUM) ? (ant_offset_t)js_getnum(head_val) : 0; ant_offset_t len = js_arr_len(js, queue); if (len <= 0 || head >= len) { - js_set_slot(js, port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum(0)); + js_set_slot(port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum(0)); return false; } @@ -148,29 +153,29 @@ static bool wt_port_queue_dequeue(ant_t *js, ant_value_t port, ant_value_t *out) ant_offset_t next_head = head + 1; if (next_head >= len) { - js_set_slot(js, port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum(0)); + js_set_slot(port, SLOT_WT_PORT_QUEUE, js_mkarr(js)); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum(0)); return true; } if (next_head > 32 && next_head * 2 >= len) { ant_value_t compact = js_mkarr(js); for (ant_offset_t i = next_head; i < len; i++) js_arr_push(js, compact, js_arr_get(js, queue, i)); - js_set_slot(js, port, SLOT_WT_PORT_QUEUE, compact); - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum(0)); + js_set_slot(port, SLOT_WT_PORT_QUEUE, compact); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum(0)); return true; } - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum((double)next_head)); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum((double)next_head)); return true; } static void wt_port_queue_push(ant_t *js, ant_value_t port, ant_value_t value) { - ant_value_t queue = js_get_slot(js, port, SLOT_WT_PORT_QUEUE); + ant_value_t queue = js_get_slot(port, SLOT_WT_PORT_QUEUE); if (vtype(queue) != T_ARR) { queue = js_mkarr(js); - js_set_slot(js, port, SLOT_WT_PORT_QUEUE, queue); - js_set_slot(js, port, SLOT_WT_PORT_HEAD, js_mknum(0)); + js_set_slot(port, SLOT_WT_PORT_QUEUE, queue); + js_set_slot(port, SLOT_WT_PORT_HEAD, js_mknum(0)); } js_arr_push(js, queue, value); } @@ -184,9 +189,9 @@ static void wt_port_call_listener(ant_t *js, ant_value_t this_obj, ant_value_t f static bool wt_port_should_deliver(ant_t *js, ant_value_t port) { if (wt_port_is_closed(js, port)) return false; - bool started = js_truthy(js, js_get_slot(js, port, SLOT_WT_PORT_STARTED)); - ant_value_t on_fn = js_get_slot(js, port, SLOT_WT_PORT_ON_MESSAGE); - ant_value_t once_fn = js_get_slot(js, port, SLOT_WT_PORT_ONCE_MESSAGE); + bool started = js_truthy(js, js_get_slot(port, SLOT_WT_PORT_STARTED)); + ant_value_t on_fn = js_get_slot(port, SLOT_WT_PORT_ON_MESSAGE); + ant_value_t once_fn = js_get_slot(port, SLOT_WT_PORT_ONCE_MESSAGE); ant_value_t onmessage = js_get(js, port, "onmessage"); bool has_event_listener = wt_is_callable(on_fn) || wt_is_callable(once_fn); @@ -201,13 +206,13 @@ static void wt_port_drain(ant_t *js, ant_value_t port) { ant_value_t msg = js_mkundef(); if (!wt_port_queue_dequeue(js, port, &msg)) break; - ant_value_t on_fn = js_get_slot(js, port, SLOT_WT_PORT_ON_MESSAGE); + ant_value_t on_fn = js_get_slot(port, SLOT_WT_PORT_ON_MESSAGE); wt_port_call_listener(js, port, on_fn, msg); - ant_value_t once_fn = js_get_slot(js, port, SLOT_WT_PORT_ONCE_MESSAGE); + ant_value_t once_fn = js_get_slot(port, SLOT_WT_PORT_ONCE_MESSAGE); if (wt_is_callable(once_fn)) { wt_port_call_listener(js, port, once_fn, msg); - js_set_slot(js, port, SLOT_WT_PORT_ONCE_MESSAGE, js_mkundef()); + js_set_slot(port, SLOT_WT_PORT_ONCE_MESSAGE, js_mkundef()); } ant_value_t onmessage = js_get(js, port, "onmessage"); @@ -237,7 +242,7 @@ static void wt_call_listener(ant_t *js, ant_value_t this_obj, ant_value_t fn, an static void wt_emit(ant_worker_thread_t *wt, const char *event, ant_value_t arg) { if (!wt || !wt->js) return; ant_t *js = wt->js; - ant_value_t this_obj = js_deref(js, wt->self_root); + ant_value_t this_obj = wt->self_val; if (!is_object_type(this_obj)) return; internal_slot_t on_slot, once_slot; @@ -249,13 +254,13 @@ static void wt_emit(ant_worker_thread_t *wt, const char *event, ant_value_t arg) once_slot = SLOT_WT_ONCE_EXIT; } else return; - ant_value_t on_fn = js_get_slot(js, this_obj, on_slot); + ant_value_t on_fn = js_get_slot(this_obj, on_slot); wt_call_listener(js, this_obj, on_fn, arg); - ant_value_t once_fn = js_get_slot(js, this_obj, once_slot); + ant_value_t once_fn = js_get_slot(this_obj, once_slot); if (wt_is_callable(once_fn)) { wt_call_listener(js, this_obj, once_fn, arg); - js_set_slot(js, this_obj, once_slot, js_mkundef()); + js_set_slot(this_obj, once_slot, js_mkundef()); } } @@ -329,23 +334,22 @@ static char **wt_build_worker_env(const char *worker_data_json, const char *env_ static void wt_cleanup(ant_worker_thread_t *wt) { if (!wt) return; - if (wt->js) { - if (wt->self_root) js_unroot(wt->js, wt->self_root); - if (wt->has_terminate_root) js_unroot(wt->js, wt->terminate_root); - } free(wt->line_buf); free(wt); } static void wt_detach(ant_worker_thread_t *wt) { if (!wt) return; - if (wt->js) { - if (wt->self_root) js_unroot(wt->js, wt->self_root); - if (wt->has_terminate_root) js_unroot(wt->js, wt->terminate_root); - } - wt->self_root = 0; - wt->terminate_root = 0; - wt->has_terminate_root = false; + + if (wt->prev) wt->prev->next = wt->next; + else active_workers_head = wt->next; + if (wt->next) wt->next->prev = wt->prev; + wt->prev = NULL; + wt->next = NULL; + + wt->self_val = js_mkundef(); + wt->terminate_val = js_mkundef(); + wt->has_terminate_val = false; free(wt->line_buf); wt->line_buf = NULL; wt->line_len = 0; @@ -391,11 +395,11 @@ static void wt_on_process_exit(uv_process_t *proc, int64_t exit_status, int term wt->exit_status = exit_status; wt->term_signal = term_signal; - if (wt->has_terminate_root) { - ant_value_t p = js_deref(wt->js, wt->terminate_root); + if (wt->has_terminate_val) { + ant_value_t p = wt->terminate_val; js_resolve_promise(wt->js, p, js_mknum((double)exit_status)); - js_root_update(wt->js, wt->terminate_root, js_mkundef()); - wt->has_terminate_root = false; + wt->terminate_val = js_mkundef(); + wt->has_terminate_val = false; } wt_emit(wt, "exit", js_mknum((double)exit_status)); @@ -587,9 +591,9 @@ static ant_value_t worker_threads_worker_on(ant_t *js, ant_value_t *args, int na if (!event) return js_mkerr(js, "invalid event name"); if (len == 7 && memcmp(event, "message", 7) == 0) { - js_set_slot(js, this_obj, SLOT_WT_ON_MESSAGE, args[1]); + js_set_slot(this_obj, SLOT_WT_ON_MESSAGE, args[1]); } else if (len == 4 && memcmp(event, "exit", 4) == 0) { - js_set_slot(js, this_obj, SLOT_WT_ON_EXIT, args[1]); + js_set_slot(this_obj, SLOT_WT_ON_EXIT, args[1]); } return this_obj; @@ -608,9 +612,9 @@ static ant_value_t worker_threads_worker_once(ant_t *js, ant_value_t *args, int if (!event) return js_mkerr(js, "invalid event name"); if (len == 7 && memcmp(event, "message", 7) == 0) { - js_set_slot(js, this_obj, SLOT_WT_ONCE_MESSAGE, args[1]); + js_set_slot(this_obj, SLOT_WT_ONCE_MESSAGE, args[1]); } else if (len == 4 && memcmp(event, "exit", 4) == 0) { - js_set_slot(js, this_obj, SLOT_WT_ONCE_EXIT, args[1]); + js_set_slot(this_obj, SLOT_WT_ONCE_EXIT, args[1]); } return this_obj; @@ -647,22 +651,22 @@ static ant_value_t worker_threads_worker_terminate(ant_t *js, ant_value_t *args, if (wt->exited) return wt_make_resolved_promise(js, js_mknum((double)wt->exit_status)); - if (!wt->has_terminate_root) { + if (!wt->has_terminate_val) { ant_value_t p = js_mkpromise(js); - wt->terminate_root = js_root(js, p); - wt->has_terminate_root = true; + wt->terminate_val = p; + wt->has_terminate_val = true; } int rc = uv_process_kill(&wt->process, SIGTERM); if (rc != 0) { - ant_value_t p = js_deref(js, wt->terminate_root); + ant_value_t p = wt->terminate_val; js_reject_promise(js, p, js_mkerr(js, "terminate failed: %s", uv_strerror(rc))); - js_root_update(js, wt->terminate_root, js_mkundef()); - wt->has_terminate_root = false; + wt->terminate_val = js_mkundef(); + wt->has_terminate_val = false; return p; } - return js_deref(js, wt->terminate_root); + return wt->terminate_val; } static ant_value_t worker_threads_worker_post_message(ant_t *js, ant_value_t *args, int nargs) { @@ -674,7 +678,7 @@ static ant_value_t worker_threads_message_port_post_message(ant_t *js, ant_value if (!wt_is_message_port(js, this_obj)) return js_mkerr(js, "invalid MessagePort receiver"); if (wt_port_is_closed(js, this_obj)) return js_mkundef(); - ant_value_t peer = js_get_slot(js, this_obj, SLOT_WT_PORT_PEER); + ant_value_t peer = js_get_slot(this_obj, SLOT_WT_PORT_PEER); if (!wt_is_message_port(js, peer) || wt_port_is_closed(js, peer)) return js_mkundef(); ant_value_t value = (nargs > 0) ? args[0] : js_mkundef(); @@ -694,8 +698,8 @@ static ant_value_t worker_threads_message_port_on(ant_t *js, ant_value_t *args, const char *event = js_getstr(js, args[0], &len); if (!event) return js_mkerr(js, "invalid event name"); if (len == 7 && memcmp(event, "message", 7) == 0) { - js_set_slot(js, this_obj, SLOT_WT_PORT_ON_MESSAGE, args[1]); - js_set_slot(js, this_obj, SLOT_WT_PORT_STARTED, js_true); + js_set_slot(this_obj, SLOT_WT_PORT_ON_MESSAGE, args[1]); + js_set_slot(this_obj, SLOT_WT_PORT_STARTED, js_true); wt_port_drain(js, this_obj); } return this_obj; @@ -712,8 +716,8 @@ static ant_value_t worker_threads_message_port_once(ant_t *js, ant_value_t *args const char *event = js_getstr(js, args[0], &len); if (!event) return js_mkerr(js, "invalid event name"); if (len == 7 && memcmp(event, "message", 7) == 0) { - js_set_slot(js, this_obj, SLOT_WT_PORT_ONCE_MESSAGE, args[1]); - js_set_slot(js, this_obj, SLOT_WT_PORT_STARTED, js_true); + js_set_slot(this_obj, SLOT_WT_PORT_ONCE_MESSAGE, args[1]); + js_set_slot(this_obj, SLOT_WT_PORT_STARTED, js_true); wt_port_drain(js, this_obj); } return this_obj; @@ -722,7 +726,7 @@ static ant_value_t worker_threads_message_port_once(ant_t *js, ant_value_t *args static ant_value_t worker_threads_message_port_start(ant_t *js, ant_value_t *args, int nargs) { ant_value_t this_obj = js_getthis(js); if (!wt_is_message_port(js, this_obj)) return js_mkerr(js, "invalid MessagePort receiver"); - js_set_slot(js, this_obj, SLOT_WT_PORT_STARTED, js_true); + js_set_slot(this_obj, SLOT_WT_PORT_STARTED, js_true); wt_port_drain(js, this_obj); return js_mkundef(); } @@ -732,9 +736,9 @@ static ant_value_t worker_threads_message_port_close(ant_t *js, ant_value_t *arg if (!wt_is_message_port(js, this_obj)) return js_mkerr(js, "invalid MessagePort receiver"); wt_port_set_closed(js, this_obj, true); - ant_value_t peer = js_get_slot(js, this_obj, SLOT_WT_PORT_PEER); - js_set_slot(js, this_obj, SLOT_WT_PORT_PEER, js_mknull()); - if (wt_is_message_port(js, peer)) js_set_slot(js, peer, SLOT_WT_PORT_PEER, js_mknull()); + ant_value_t peer = js_get_slot(this_obj, SLOT_WT_PORT_PEER); + js_set_slot(this_obj, SLOT_WT_PORT_PEER, js_mknull()); + if (wt_is_message_port(js, peer)) js_set_slot(peer, SLOT_WT_PORT_PEER, js_mknull()); return js_mkundef(); } @@ -758,8 +762,8 @@ static ant_value_t worker_threads_message_channel_ctor(ant_t *js, ant_value_t *a ant_value_t this_obj = js_getthis(js); ant_value_t port1 = wt_make_message_port(js); ant_value_t port2 = wt_make_message_port(js); - js_set_slot(js, port1, SLOT_WT_PORT_PEER, port2); - js_set_slot(js, port2, SLOT_WT_PORT_PEER, port1); + js_set_slot(port1, SLOT_WT_PORT_PEER, port2); + js_set_slot(port2, SLOT_WT_PORT_PEER, port1); js_set(js, this_obj, "port1", port1); js_set(js, this_obj, "port2", port2); return this_obj; @@ -835,8 +839,13 @@ static ant_value_t worker_threads_worker_ctor(ant_t *js, ant_value_t *args, int } wt->js = js; - wt->self_root = js_root(js, this_obj); - js_set_slot(js, this_obj, SLOT_DATA, ANT_PTR(wt)); + wt->self_val = this_obj; + js_set_slot(this_obj, SLOT_DATA, ANT_PTR(wt)); + + wt->prev = NULL; + wt->next = active_workers_head; + if (active_workers_head) active_workers_head->prev = wt; + active_workers_head = wt; int rc = wt_spawn_worker(wt, script_path, worker_data_json, env_store_json); free(script_path); @@ -844,7 +853,7 @@ static ant_value_t worker_threads_worker_ctor(ant_t *js, ant_value_t *args, int free(env_store_heap); if (rc != 0) { - js_set_slot(js, this_obj, SLOT_DATA, js_mkundef()); + js_set_slot(this_obj, SLOT_DATA, js_mkundef()); wt_cleanup(wt); return js_mkerr(js, "Failed to spawn Worker: %s", uv_strerror(rc)); } @@ -958,6 +967,13 @@ static ant_value_t worker_threads_move_message_port_to_context(ant_t *js, ant_va return args[0]; } +void gc_mark_worker_threads(ant_t *js, gc_mark_fn mark) { + for (ant_worker_thread_t *wt = active_workers_head; wt; wt = wt->next) { + mark(js, wt->self_val); + if (wt->has_terminate_val) mark(js, wt->terminate_val); + } +} + ant_value_t worker_threads_library(ant_t *js) { ant_value_t lib = js_mkobj(js); bool is_worker = wt_is_worker_mode(); @@ -979,17 +995,17 @@ ant_value_t worker_threads_library(ant_t *js) { js_set(js, message_port_proto, "unref", js_mkfun(worker_threads_message_port_unref)); js_set_sym(js, message_port_proto, get_toStringTag_sym(), js_mkstr(js, "MessagePort", 11)); - js_set_slot(js, message_port_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_message_port_ctor)); + js_set_slot(message_port_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_message_port_ctor)); js_mkprop_fast(js, message_port_ctor_obj, "prototype", 9, message_port_proto); js_mkprop_fast(js, message_port_ctor_obj, "name", 4, js_mkstr(js, "MessagePort", 11)); js_set_descriptor(js, message_port_ctor_obj, "name", 4, 0); js_set(js, lib, "MessagePort", js_obj_to_func(message_port_ctor_obj)); - js_set_slot(js, js->global, SLOT_WT_PORT_PROTO, message_port_proto); + js_set_slot(js->global, SLOT_WT_PORT_PROTO, message_port_proto); ant_value_t message_channel_ctor_obj = js_mkobj(js); ant_value_t message_channel_proto = js_mkobj(js); js_set_sym(js, message_channel_proto, get_toStringTag_sym(), js_mkstr(js, "MessageChannel", 14)); - js_set_slot(js, message_channel_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_message_channel_ctor)); + js_set_slot(message_channel_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_message_channel_ctor)); js_mkprop_fast(js, message_channel_ctor_obj, "prototype", 9, message_channel_proto); js_mkprop_fast(js, message_channel_ctor_obj, "name", 4, js_mkstr(js, "MessageChannel", 14)); js_set_descriptor(js, message_channel_ctor_obj, "name", 4, 0); @@ -1025,7 +1041,7 @@ ant_value_t worker_threads_library(ant_t *js) { js_set(js, worker_proto, "postMessage", js_mkfun(worker_threads_worker_post_message)); js_set_sym(js, worker_proto, get_toStringTag_sym(), js_mkstr(js, "Worker", 6)); - js_set_slot(js, worker_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_worker_ctor)); + js_set_slot(worker_ctor_obj, SLOT_CFUNC, js_mkfun(worker_threads_worker_ctor)); js_mkprop_fast(js, worker_ctor_obj, "prototype", 9, worker_proto); js_mkprop_fast(js, worker_ctor_obj, "name", 4, js_mkstr(js, "Worker", 6)); js_set_descriptor(js, worker_ctor_obj, "name", 4, 0); diff --git a/src/pkg/extractor.zig b/src/pkg/extractor.zig index e3d674b..d62f2dc 100644 --- a/src/pkg/extractor.zig +++ b/src/pkg/extractor.zig @@ -3,7 +3,7 @@ const builtin = @import("builtin"); const linker = @import("linker.zig"); const c = @cImport({ - @cInclude("zlib-ng.h"); + @cInclude("zlib.h"); }); pub const ExtractError = error{ @@ -146,7 +146,7 @@ pub const TarHeader = extern struct { }; pub const GzipDecompressor = struct { - stream: c.zng_stream, + stream: c.z_stream, initialized: bool, allocator: std.mem.Allocator, @@ -155,10 +155,10 @@ pub const GzipDecompressor = struct { errdefer allocator.destroy(self); self.allocator = allocator; - self.stream = std.mem.zeroes(c.zng_stream); + self.stream = std.mem.zeroes(c.z_stream); self.initialized = false; - const ret = c.zng_inflateInit2(&self.stream, 15 + 32); + const ret = c.inflateInit2(&self.stream, 15 + 32); if (ret != c.Z_OK) { allocator.destroy(self); return error.DecompressionFailed; @@ -169,7 +169,7 @@ pub const GzipDecompressor = struct { } pub fn deinit(self: *GzipDecompressor) void { - if (self.initialized) _ = c.zng_inflateEnd(&self.stream); + if (self.initialized) _ = c.inflateEnd(&self.stream); self.allocator.destroy(self); } @@ -188,7 +188,7 @@ pub const GzipDecompressor = struct { self.stream.next_out = &output_buf; self.stream.avail_out = output_buf.len; - const ret = c.zng_inflate(&self.stream, c.Z_NO_FLUSH); + const ret = c.inflate(&self.stream, c.Z_NO_FLUSH); if (ret == c.Z_STREAM_END) { const produced = output_buf.len - self.stream.avail_out; diff --git a/src/pool.c b/src/pool.c new file mode 100644 index 0000000..f2ceb7f --- /dev/null +++ b/src/pool.c @@ -0,0 +1,264 @@ +#include "internal.h" +#include "gc.h" + +#include +#include + +static const size_t g_size_classes[ANT_POOL_SIZE_CLASS_COUNT] = { + 16u, 24u, 32u, 40u, 48u, 56u, 64u, 80u, + 96u, 112u, 128u, 160u, 192u, 224u, 256u, 320u, + 384u, 448u, 512u, 640u, 768u, 896u, 1024u, 1280u, + 1536u, 1792u, 2048u, 2560u, 3072u, 4096u, 8192u, 16384u +}; + +static inline bool pool_kind_uses_size_classes(ant_alloc_kind_t kind) { + return kind == ANT_ALLOC_STRING || kind == ANT_ALLOC_BIGINT; +} + +static inline size_t pool_default_block_size(ant_alloc_kind_t kind) { +switch (kind) { + case ANT_ALLOC_SYMBOL: return ANT_POOL_SYMBOL_BLOCK_SIZE; + case ANT_ALLOC_BIGINT: return ANT_POOL_BIGINT_BLOCK_SIZE; + case ANT_ALLOC_STRING: return ANT_POOL_STRING_BLOCK_SIZE; + case ANT_ALLOC_ROPE: + default: return ANT_POOL_ROPE_BLOCK_SIZE; +}} + +static inline ant_pool_t *pool_for_kind(ant_t *js, ant_alloc_kind_t kind) { +switch (kind) { + case ANT_ALLOC_SYMBOL: return &js->pool.symbol; + case ANT_ALLOC_BIGINT: return &js->pool.bigint.base; + case ANT_ALLOC_STRING: return &js->pool.string.base; + case ANT_ALLOC_ROPE: + default: return &js->pool.rope; +}} + +static inline ant_class_pool_t *class_pool_for_kind(ant_t *js, ant_alloc_kind_t kind) { +switch (kind) { + case ANT_ALLOC_BIGINT: return &js->pool.bigint; + case ANT_ALLOC_STRING: return &js->pool.string; + default: return NULL; +}} + +static inline size_t align_up_size(size_t value, size_t align) { + if (align == 0) return value; + size_t mask = align - 1; + return (value + mask) & ~mask; +} + +static inline int pool_size_class_index(size_t need) { + for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { + if (need <= g_size_classes[i]) return i; + } + return -1; +} + +static inline size_t pool_class_block_items(size_t class_size) { + if (class_size <= 256u) return 256u; + if (class_size <= 1024u) return 128u; + if (class_size <= 4096u) return 64u; + return 32u; +} + +static void *pool_bucket_alloc_fast( + ant_pool_bucket_t *bucket, + size_t class_size, + size_t align +) { + if (!bucket) return NULL; + + const size_t cache_align = _Alignof(max_align_t); + if (align > cache_align) return NULL; + + if (bucket->slot_stride == 0) { + bucket->slot_stride = align_up_size(class_size, cache_align); + } + + if (bucket->slot_free) { + void *ptr = bucket->slot_free; + void *next; + + memcpy(&next, ptr, sizeof(void *)); + bucket->slot_free = next; + + return ptr; + } + + if (!bucket->current || !bucket->cursor || bucket->cursor + bucket->slot_stride > bucket->end) { + if (bucket->free_head) { + ant_pool_block_t *reuse = bucket->free_head; + bucket->free_head = pool_free_next(reuse); + reuse->used = 0; + reuse->prev = NULL; + reuse->next = bucket->head; + if (bucket->head) bucket->head->prev = reuse; + bucket->head = reuse; + bucket->current = reuse; + bucket->cursor = reuse->data; + bucket->end = reuse->data + reuse->cap; + } else { + size_t cap = bucket->block_size; + if (cap < bucket->slot_stride) cap = bucket->slot_stride; + ant_pool_block_t *block = pool_block_alloc(cap); + if (!block) return NULL; + block->next = bucket->head; + if (bucket->head) bucket->head->prev = block; + bucket->head = block; + bucket->current = block; + bucket->cursor = block->data; + bucket->end = block->data + cap; + }} + + uint8_t *ptr = bucket->cursor; + bucket->cursor += bucket->slot_stride; + if (bucket->current) bucket->current->used = (size_t)(bucket->cursor - bucket->current->data); + + return ptr; +} + +void *pool_alloc_chain( + ant_pool_block_t **head, + ant_pool_block_t **free_head, + size_t block_size, + size_t size, + size_t align +) { + if (!head || size == 0) return NULL; + if (align == 0) align = sizeof(void *); + + ant_pool_block_t *block = *head; + if (!block) { + size_t cap = block_size; + if (cap < size + align) cap = size + align; + block = pool_block_alloc(cap); + if (!block) return NULL; + *head = block; + } + + uintptr_t base = (uintptr_t)block->data; + size_t start = align_up_size(block->used, align); + if (start + size > block->cap) { + if (free_head && *free_head) { + block = *free_head; + *free_head = pool_free_next(block); + block->used = 0; + block->prev = NULL; + block->next = *head; + if (*head) (*head)->prev = block; + *head = block; + } else { + size_t cap = block_size; + if (cap < size + align) cap = size + align; + ant_pool_block_t *next = pool_block_alloc(cap); + if (!next) return NULL; + next->next = block; + if (block) block->prev = next; + *head = next; + block = next; + } + base = (uintptr_t)block->data; + start = align_up_size(block->used, align); + } + + void *ptr = (void *)(base + start); + block->used = start + size; + return ptr; +} + +void *js_type_alloc(ant_t *js, ant_alloc_kind_t kind, size_t size, size_t align) { + if (!js || size == 0) return NULL; + if (align == 0) align = sizeof(void *); + + js->gc_pool_alloc += size; + size_t pool_threshold = GC_HEAP_GROWTH(js->gc_pool_last_live); + + if (pool_threshold < (4u * 1024u * 1024u)) pool_threshold = 4u * 1024u * 1024u; + if (js->gc_pool_alloc >= pool_threshold) gc_run(js); + + ant_pool_t *pool = pool_for_kind(js, kind); + if (pool->block_size == 0) pool->block_size = pool_default_block_size(kind); + + if (pool_kind_uses_size_classes(kind)) { + ant_class_pool_t *class_pool = class_pool_for_kind(js, kind); + if (!class_pool) return pool_alloc_chain(&pool->head, &pool->free_head, pool->block_size, size, align); + + size_t needed = size + (align - 1u); + int class_idx = pool_size_class_index(needed); + if (class_idx >= 0) { + size_t class_size = g_size_classes[class_idx]; + ant_pool_bucket_t *bucket = &class_pool->classes[class_idx]; + if (bucket->block_size == 0) { + size_t bucket_block_size = pool->block_size; + size_t min_block = class_size * pool_class_block_items(class_size); + if (bucket_block_size < min_block) bucket_block_size = min_block; + bucket->block_size = bucket_block_size; + } + void *fast = pool_bucket_alloc_fast(bucket, class_size, align); + if (fast) return fast; + return pool_alloc_chain(&bucket->head, &bucket->free_head, bucket->block_size, size, align); + } + } + + return pool_alloc_chain(&pool->head, &pool->free_head, pool->block_size, size, align); +} + +void js_pool_destroy(ant_pool_t *pool) { + if (!pool) return; + + ant_pool_block_t *block = pool->head; + while (block) { + ant_pool_block_t *next = block->next; + pool_block_free(block); + block = next; + } + pool->head = NULL; + pool->block_size = 0; +} + +ant_pool_stats_t js_pool_stats(ant_pool_t *pool) { + ant_pool_stats_t s = {0}; + if (!pool) return s; + for (ant_pool_block_t *b = pool->head; b; b = b->next) { + s.used += b->used; + s.capacity += b->cap; + s.blocks++; + } + return s; +} + +static inline void pool_stats_add(ant_pool_stats_t *dst, ant_pool_stats_t src) { + dst->used += src.used; + dst->capacity += src.capacity; + dst->blocks += src.blocks; +} + +ant_pool_stats_t js_class_pool_stats(ant_class_pool_t *pool) { + ant_pool_stats_t s = {0}; + if (!pool) return s; + for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { + ant_pool_t bucket = { .head = pool->classes[i].head }; + pool_stats_add(&s, js_pool_stats(&bucket)); + } + pool_stats_add(&s, js_pool_stats(&pool->base)); + return s; +} + +void js_class_pool_destroy(ant_class_pool_t *pool) { + if (!pool) return; + + for (int i = 0; i < ANT_POOL_SIZE_CLASS_COUNT; i++) { + ant_pool_block_t *block = pool->classes[i].head; + while (block) { + ant_pool_block_t *next = block->next; + pool_block_free(block); + block = next; + } + pool->classes[i].head = NULL; + pool->classes[i].block_size = 0; + pool->classes[i].cursor = NULL; + pool->classes[i].end = NULL; + pool->classes[i].slot_stride = 0; + } + + js_pool_destroy(&pool->base); +} diff --git a/src/reactor.c b/src/reactor.c index a679f22..abd5b0f 100644 --- a/src/reactor.c +++ b/src/reactor.c @@ -42,7 +42,8 @@ void js_reactor_set_poll_hook(reactor_poll_hook_t hook, void *data) { void js_poll_events(ant_t *js) { coros_this_tick = 0; - + gc_maybe(js); + process_immediates(js); process_microtasks(js); @@ -64,7 +65,6 @@ void js_poll_events(ant_t *js) { } } - if (js->needs_gc) js_gc_maybe(js); if (g_poll_hook) g_poll_hook(g_poll_hook_data); } @@ -76,7 +76,6 @@ drain: if (work & WORK_BLOCKING) uv_run(uv_default_loop(), UV_RUN_NOWAIT); else if ((work & WORK_ASYNC) || UV_CHECK_ALIVE) { - js_gc_maybe(js); uv_run(uv_default_loop(), UV_RUN_ONCE); } else break; } diff --git a/src/repl.c b/src/repl.c index 9b985e8..712f32f 100644 --- a/src/repl.c +++ b/src/repl.c @@ -9,6 +9,7 @@ #include "reactor.h" #include "runtime.h" #include "internal.h" +#include "descriptors.h" #include "silver/ast.h" #include "silver/engine.h" @@ -617,6 +618,9 @@ void ant_repl_run() { js_set(js, js_glob(js), "_", js_mkundef()); js_set(js, js_glob(js), "_error", js_mkundef()); + + js_set_descriptor(js, js_as_obj(js_glob(js)), "_", 1, JS_DESC_W | JS_DESC_C); + js_set_descriptor(js, js_as_obj(js_glob(js)), "_error", 6, JS_DESC_W | JS_DESC_C); int prev_ctrl_c_count = 0; char *multiline_buf = NULL; diff --git a/src/roots.c b/src/roots.c deleted file mode 100644 index 0ab89fa..0000000 --- a/src/roots.c +++ /dev/null @@ -1,33 +0,0 @@ -#include "ant.h" -#include "gc.h" -#include "arena.h" -#include "internal.h" // IWYU pragma: keep - -ant_handle_t js_root(ant_t *js, ant_value_t val) { - if (js->gc_roots_len >= js->gc_roots_cap) { - ant_handle_t new_cap = js->gc_roots_cap ? js->gc_roots_cap * 2 : GC_ROOTS_INITIAL_CAP; - ant_value_t *new_roots = ant_realloc(js->gc_roots, new_cap * sizeof(ant_value_t)); - if (!new_roots) { - fprintf(stderr, "FATAL: Out of memory allocating GC roots\n"); - abort(); - } - js->gc_roots = new_roots; - js->gc_roots_cap = new_cap; - } - ant_handle_t idx = js->gc_roots_len++; - js->gc_roots[idx] = val; - return idx; -} - -ant_value_t js_deref(ant_t *js, ant_handle_t h) { - if (h < 0 || h >= js->gc_roots_len) return js_mkundef(); - return js->gc_roots[h]; -} - -void js_unroot(ant_t *js, ant_handle_t h) { - if (h >= 0 && h == js->gc_roots_len - 1) js->gc_roots_len--; -} - -void js_root_update(ant_t *js, ant_handle_t h, ant_value_t val) { - if (h >= 0 && h < js->gc_roots_len) js->gc_roots[h] = val; -} \ No newline at end of file diff --git a/src/runtime.c b/src/runtime.c index 8c8754a..89bef62 100644 --- a/src/runtime.c +++ b/src/runtime.c @@ -1,10 +1,11 @@ #include "ant.h" +#include "runtime.h" +#include "descriptors.h" +#include "silver/engine.h" #include #include #include -#include -#include #include #include @@ -172,10 +173,14 @@ struct ant_runtime *ant_runtime_init(ant_t *js, int argc, char **argv, struct ar }; ant_value_t global = js_glob(js); - js_set(js, global, "global", global); js_set(js, global, "window", global); + js_set(js, global, "global", global); js_set(js, global, "globalThis", global); js_set(js, global, "Ant", runtime.ant_obj); + js_set_descriptor(js, js_as_obj(global), "window", 6, JS_DESC_W | JS_DESC_C); + js_set_descriptor(js, js_as_obj(global), "global", 6, JS_DESC_W | JS_DESC_C); + js_set_descriptor(js, js_as_obj(global), "globalThis", 10, JS_DESC_W | JS_DESC_C); + return &runtime; } diff --git a/src/shapes.c b/src/shapes.c new file mode 100644 index 0000000..06a6fac --- /dev/null +++ b/src/shapes.c @@ -0,0 +1,535 @@ +#include "shapes.h" +#include +#include +#include +#include + +uint32_t ant_ic_epoch_counter = 1; + +#define SHAPE_ENTRY_SIZE sizeof(shape_index_entry_t) +#define SHAPE_ENTRY_POOL_MAX 1024 + +static size_t g_shape_bytes = 0; +static size_t g_shape_entry_pool_len = 0; +static void *g_shape_entry_pool = NULL; + +typedef struct shape_index_entry { + uint64_t key; + uint32_t slot; + UT_hash_handle hh; +} shape_index_entry_t; + + +typedef struct shape_child_entry { + uint64_t key; + ant_shape_t *child; + UT_hash_handle hh; +} shape_child_entry_t; + +_Static_assert( + sizeof(shape_index_entry_t) == sizeof(shape_child_entry_t), + "entry pool requires index and child entries to be the same size" +); + +static inline void *shape_entry_alloc(void) { + if (g_shape_entry_pool) { + void *p = g_shape_entry_pool; + g_shape_entry_pool = *(void **)p; + g_shape_entry_pool_len--; + memset(p, 0, SHAPE_ENTRY_SIZE); + return p; + } + return calloc(1, SHAPE_ENTRY_SIZE); +} + +static inline void shape_entry_free(void *p) { + if (!p) return; + if (g_shape_entry_pool_len < SHAPE_ENTRY_POOL_MAX) { + *(void **)p = g_shape_entry_pool; + g_shape_entry_pool = p; + g_shape_entry_pool_len++; + } else free(p); +} + +static inline void shape_entry_pool_trim(void) { + size_t keep = g_shape_entry_pool_len / 2; + while (g_shape_entry_pool_len > keep) { + void *p = g_shape_entry_pool; + g_shape_entry_pool = *(void **)p; + g_shape_entry_pool_len--; + free(p); + } +} + +struct ant_shape { + uint32_t ref_count; + uint32_t count; + uint32_t cap; + uint8_t inobj_limit; + uint16_t gc_mark; + + ant_shape_prop_t *props; + shape_index_entry_t *index; + shape_child_entry_t *children; + ant_shape_t *parent; + + uint64_t parent_key; +}; + +static ant_shape_t *g_root_shapes[ANT_INOBJ_MAX_SLOTS + 1]; +static inline uint8_t shape_clamp_inobj_limit(uint8_t limit) { + return (limit > ANT_INOBJ_MAX_SLOTS) ? (uint8_t)ANT_INOBJ_MAX_SLOTS : limit; +} + +static uint64_t shape_key_interned(const char *interned) { + return ((uint64_t)(uintptr_t)interned << 1); +} + +static uint64_t shape_key_symbol(ant_offset_t sym_off) { + return ((uint64_t)sym_off << 1) | 1u; +} + +static uint64_t shape_child_key(uint64_t prop_key, uint8_t attrs) { + return prop_key ^ ((uint64_t)attrs << 56); +} + +static shape_index_entry_t *shape_lookup(const ant_shape_t *shape, uint64_t key) { + if (!shape) return NULL; + shape_index_entry_t *entry = NULL; + HASH_FIND(hh, shape->index, &key, sizeof(key), entry); + return entry; +} + +static bool shape_rebuild_index(ant_shape_t *shape) { + if (!shape) return false; + + shape_index_entry_t *entry, *tmp; + HASH_ITER(hh, shape->index, entry, tmp) { + HASH_DEL(shape->index, entry); + shape_entry_free(entry); + } + shape->index = NULL; + + for (uint32_t i = 0; i < shape->count; i++) { + const ant_shape_prop_t *prop = &shape->props[i]; + + uint64_t key = (prop->type == ANT_SHAPE_KEY_SYMBOL) + ? shape_key_symbol(prop->key.sym_off) + : shape_key_interned(prop->key.interned); + + shape_index_entry_t *idx = shape_entry_alloc(); + if (!idx) return false; + + g_shape_bytes += sizeof(*idx); + idx->key = key; idx->slot = i; + HASH_ADD(hh, shape->index, key, sizeof(key), idx); + } + + return true; +} + +static bool shape_ensure_capacity(ant_shape_t *shape, uint32_t needed) { + if (!shape) return false; + if (needed <= shape->cap) return true; + + uint32_t new_cap = shape->cap ? shape->cap * 2 : 8; + while (new_cap < needed) new_cap *= 2; + + ant_shape_prop_t *next = realloc(shape->props, sizeof(*next) * new_cap); + if (!next) return false; + + g_shape_bytes += (new_cap - shape->cap) * sizeof(*next); + shape->props = next; + shape->cap = new_cap; + return true; +} + +static bool shape_add_key( + ant_shape_t *shape, + ant_shape_key_type_t type, + const char *interned, + ant_offset_t sym_off, + uint8_t attrs, + uint32_t *out_slot +) { + if (!shape) return false; + uint64_t key = (type == ANT_SHAPE_KEY_SYMBOL) + ? shape_key_symbol(sym_off) + : shape_key_interned(interned); + + shape_index_entry_t *found = shape_lookup(shape, key); + if (found) { + shape->props[found->slot].attrs = attrs; + ant_ic_epoch_bump(); + if (out_slot) *out_slot = found->slot; + return true; + } + + if (!shape_ensure_capacity(shape, shape->count + 1)) return false; + uint32_t slot = shape->count++; + ant_shape_prop_t *prop = &shape->props[slot]; + + prop->type = type; + prop->attrs = attrs; + prop->has_getter = 0; + prop->has_setter = 0; + prop->getter = 0; + prop->setter = 0; + + if (type == ANT_SHAPE_KEY_SYMBOL) prop->key.sym_off = sym_off; + else prop->key.interned = interned; + + shape_index_entry_t *idx = shape_entry_alloc(); + if (!idx) return false; + + g_shape_bytes += sizeof(*idx); + idx->key = key; idx->slot = slot; + HASH_ADD(hh, shape->index, key, sizeof(key), idx); + + if (out_slot) *out_slot = slot; + return true; +} + +static ant_shape_t *shape_find_child(ant_shape_t *shape, uint64_t ckey) { + if (!shape || !shape->children) return NULL; + shape_child_entry_t *entry = NULL; + HASH_FIND(hh, shape->children, &ckey, sizeof(ckey), entry); + return entry ? entry->child : NULL; +} + +static void shape_record_child(ant_shape_t *parent, uint64_t ckey, ant_shape_t *child) { + shape_child_entry_t *existing = NULL; + HASH_FIND(hh, parent->children, &ckey, sizeof(ckey), existing); + if (existing) return; + + shape_child_entry_t *entry = shape_entry_alloc(); + if (!entry) return; + + entry->key = ckey; + entry->child = child; + ant_shape_retain(child); + HASH_ADD(hh, parent->children, key, sizeof(entry->key), entry); + child->parent = parent; + child->parent_key = ckey; +} + + +static inline bool shape_is_in_tree(const ant_shape_t *shape) { + return shape && (shape->ref_count > 1 || shape->parent != NULL); +} + +bool ant_shape_add_interned_tr(ant_shape_t **shape_pp, const char *interned, uint8_t attrs, uint32_t *out_slot) { + ant_shape_t *shape = *shape_pp; + + if (!shape) return false; + if (!shape_is_in_tree(shape)) { + return shape_add_key(shape, ANT_SHAPE_KEY_STRING, interned, 0, attrs, out_slot); + } + + uint64_t prop_key = shape_key_interned(interned); + uint64_t ckey = shape_child_key(prop_key, attrs); + ant_shape_t *child = shape_find_child(shape, ckey); + + if (child) { + int32_t slot = ant_shape_lookup_interned(child, interned); + if (slot >= 0) { + if (out_slot) *out_slot = (uint32_t)slot; + ant_shape_retain(child); ant_shape_release(shape); + *shape_pp = child; return true; + } + } + + ant_shape_t *shared = ant_shape_clone(shape); + if (!shared) return false; + if (!shape_add_key(shared, ANT_SHAPE_KEY_STRING, interned, 0, attrs, out_slot)) { + ant_shape_release(shared); + return false; + } + + shape_record_child(shape, ckey, shared); + ant_shape_release(shared); + + ant_shape_t *next = ant_shape_clone(shared); + if (!next) return false; + + ant_shape_release(shape); + *shape_pp = next; + + return true; +} + +bool ant_shape_add_symbol_tr(ant_shape_t **shape_pp, ant_offset_t sym_off, uint8_t attrs, uint32_t *out_slot) { + ant_shape_t *shape = *shape_pp; + + if (!shape) return false; + if (!shape_is_in_tree(shape)) { + return shape_add_key(shape, ANT_SHAPE_KEY_SYMBOL, NULL, sym_off, attrs, out_slot); + } + + uint64_t prop_key = shape_key_symbol(sym_off); + uint64_t ckey = shape_child_key(prop_key, attrs); + ant_shape_t *child = shape_find_child(shape, ckey); + + if (child) { + int32_t slot = ant_shape_lookup_symbol(child, sym_off); + if (slot >= 0) { + if (out_slot) *out_slot = (uint32_t)slot; + ant_shape_retain(child); ant_shape_release(shape); + *shape_pp = child; return true; + } + } + + ant_shape_t *shared = ant_shape_clone(shape); + if (!shared) return false; + if (!shape_add_key(shared, ANT_SHAPE_KEY_SYMBOL, NULL, sym_off, attrs, out_slot)) { + ant_shape_release(shared); + return false; + } + + shape_record_child(shape, ckey, shared); + ant_shape_release(shared); + + ant_shape_t *next = ant_shape_clone(shared); + if (!next) return false; + + ant_shape_release(shape); + *shape_pp = next; + + return true; +} + +ant_shape_t *ant_shape_new_with_inobj_limit(uint8_t inobj_limit) { + uint8_t clamped = shape_clamp_inobj_limit(inobj_limit); + + if (!g_root_shapes[clamped]) { + ant_shape_t *s = calloc(1, sizeof(*s)); + if (!s) return NULL; + g_shape_bytes += sizeof(*s); + s->inobj_limit = clamped; + s->ref_count = 1; + g_root_shapes[clamped] = s; + } + + ant_shape_retain(g_root_shapes[clamped]); + return g_root_shapes[clamped]; +} + +ant_shape_t *ant_shape_new(void) { + return ant_shape_new_with_inobj_limit((uint8_t)ANT_INOBJ_MAX_SLOTS); +} + +ant_shape_t *ant_shape_clone(const ant_shape_t *shape) { + if (!shape) return NULL; + + ant_shape_t *copy = calloc(1, sizeof(*copy)); + if (!copy) return NULL; + + g_shape_bytes += sizeof(*copy); + copy->ref_count = 1; + copy->inobj_limit = shape_clamp_inobj_limit(shape->inobj_limit); + + if (shape->count > 0) { + if (!shape_ensure_capacity(copy, shape->count)) { + ant_shape_release(copy); + return NULL; + } + + memcpy(copy->props, shape->props, sizeof(*shape->props) * shape->count); + copy->count = shape->count; + + if (!shape_rebuild_index(copy)) { + ant_shape_release(copy); + return NULL; + }} + + return copy; +} + +bool ant_shape_is_shared(const ant_shape_t *shape) { + return shape && shape->ref_count > 1; +} + +void ant_shape_retain(ant_shape_t *shape) { + if (!shape) return; + shape->ref_count++; +} + +void ant_shape_release(ant_shape_t *shape) { + if (!shape) return; + if (shape->ref_count == 0 || --shape->ref_count != 0) return; + + if (shape->parent) { + shape_child_entry_t *entry = NULL; + HASH_FIND(hh, shape->parent->children, &shape->parent_key, sizeof(shape->parent_key), entry); + if (entry) { + HASH_DEL(shape->parent->children, entry); + shape_entry_free(entry); + } + shape->parent = NULL; + } + + shape_child_entry_t *ce, *ctmp; + HASH_ITER(hh, shape->children, ce, ctmp) { + if (ce->child) { + ce->child->parent = NULL; + ant_shape_release(ce->child); + } + HASH_DEL(shape->children, ce); + shape_entry_free(ce); + } + + shape_index_entry_t *entry, *tmp; + HASH_ITER(hh, shape->index, entry, tmp) { + HASH_DEL(shape->index, entry); + g_shape_bytes -= sizeof(*entry); + shape_entry_free(entry); + } + + g_shape_bytes -= shape->cap * sizeof(*shape->props); + g_shape_bytes -= sizeof(*shape); + + free(shape->props); + free(shape); +} + +size_t ant_shape_total_bytes(void) { return g_shape_bytes; } +static uint16_t gc_shape_epoch = 0; + +void ant_gc_shapes_begin(void) { + gc_shape_epoch++; + if (gc_shape_epoch == 0) gc_shape_epoch = 1; +} + +void ant_gc_shapes_mark(ant_shape_t *shape) { +for (; shape; shape = shape->parent) { + if (shape->gc_mark == gc_shape_epoch) break; + shape->gc_mark = gc_shape_epoch; +}} + +static void shape_prune_dead_children(ant_shape_t *shape) { + shape_child_entry_t *ce, *ctmp; + HASH_ITER(hh, shape->children, ce, ctmp) { + shape_prune_dead_children(ce->child); + + if (ce->child->gc_mark != gc_shape_epoch && !ce->child->children) { + ant_shape_t *child = ce->child; + child->parent = NULL; + HASH_DEL(shape->children, ce); + shape_entry_free(ce); + ant_shape_release(child); + } + } +} + +void ant_gc_shapes_sweep(void) { + for (int i = 0; i <= (int)ANT_INOBJ_MAX_SLOTS; i++) { + if (g_root_shapes[i]) shape_prune_dead_children(g_root_shapes[i]); + } + shape_entry_pool_trim(); +} + +uint8_t ant_shape_get_inobj_limit(const ant_shape_t *shape) { + if (!shape) return (uint8_t)ANT_INOBJ_MAX_SLOTS; + return shape_clamp_inobj_limit(shape->inobj_limit); +} + +int32_t ant_shape_lookup_interned(const ant_shape_t *shape, const char *interned) { + shape_index_entry_t *entry = shape_lookup(shape, shape_key_interned(interned)); + return entry ? (int32_t)entry->slot : -1; +} + +int32_t ant_shape_lookup_symbol(const ant_shape_t *shape, ant_offset_t sym_off) { + shape_index_entry_t *entry = shape_lookup(shape, shape_key_symbol(sym_off)); + return entry ? (int32_t)entry->slot : -1; +} + +bool ant_shape_add_interned(ant_shape_t *shape, const char *interned, uint8_t attrs, uint32_t *out_slot) { + return shape_add_key(shape, ANT_SHAPE_KEY_STRING, interned, 0, attrs, out_slot); +} + +bool ant_shape_add_symbol(ant_shape_t *shape, ant_offset_t sym_off, uint8_t attrs, uint32_t *out_slot) { + return shape_add_key(shape, ANT_SHAPE_KEY_SYMBOL, NULL, sym_off, attrs, out_slot); +} + +bool ant_shape_remove_slot(ant_shape_t *shape, uint32_t slot, uint32_t *swapped_from) { + if (!shape || slot >= shape->count) return false; + if (swapped_from) *swapped_from = slot; + + const ant_shape_prop_t *dp = &shape->props[slot]; + uint64_t del_key = (dp->type == ANT_SHAPE_KEY_SYMBOL) + ? shape_key_symbol(dp->key.sym_off) + : shape_key_interned(dp->key.interned); + + shape_index_entry_t *del_entry = NULL; + HASH_FIND(hh, shape->index, &del_key, sizeof(del_key), del_entry); + if (del_entry) { + HASH_DEL(shape->index, del_entry); + shape_entry_free(del_entry); + } + + uint32_t last = shape->count - 1; + if (slot != last) { + shape->props[slot] = shape->props[last]; + + const ant_shape_prop_t *sp = &shape->props[slot]; + uint64_t swap_key = (sp->type == ANT_SHAPE_KEY_SYMBOL) + ? shape_key_symbol(sp->key.sym_off) + : shape_key_interned(sp->key.interned); + + shape_index_entry_t *swap_entry = NULL; + HASH_FIND(hh, shape->index, &swap_key, sizeof(swap_key), swap_entry); + if (swap_entry) swap_entry->slot = slot; + if (swapped_from) *swapped_from = last; + } + + shape->count--; + ant_ic_epoch_bump(); + + return true; +} + +uint32_t ant_shape_count(const ant_shape_t *shape) { + return shape ? shape->count : 0; +} + +const ant_shape_prop_t *ant_shape_prop_at(const ant_shape_t *shape, uint32_t slot) { + if (!shape || slot >= shape->count) return NULL; + return &shape->props[slot]; +} + +ant_shape_prop_t *ant_shape_prop_mut_at(ant_shape_t *shape, uint32_t slot) { + if (!shape || slot >= shape->count) return NULL; + return &shape->props[slot]; +} + +bool ant_shape_set_attrs_interned(ant_shape_t *shape, const char *interned, uint8_t attrs) { + int32_t slot = ant_shape_lookup_interned(shape, interned); + if (slot < 0) return false; + shape->props[(uint32_t)slot].attrs = attrs; + ant_ic_epoch_bump(); + return true; +} + +bool ant_shape_set_attrs_symbol(ant_shape_t *shape, ant_offset_t sym_off, uint8_t attrs) { + int32_t slot = ant_shape_lookup_symbol(shape, sym_off); + if (slot < 0) return false; + shape->props[(uint32_t)slot].attrs = attrs; + ant_ic_epoch_bump(); + return true; +} + +uint8_t ant_shape_get_attrs(const ant_shape_t *shape, uint32_t slot) { + const ant_shape_prop_t *prop = ant_shape_prop_at(shape, slot); + return prop ? prop->attrs : ANT_PROP_ATTR_DEFAULT; +} + +bool ant_shape_clear_accessor_slot(ant_shape_t *shape, uint32_t slot) { + ant_shape_prop_t *prop = ant_shape_prop_mut_at(shape, slot); + if (!prop) return false; + prop->has_getter = 0; + prop->has_setter = 0; + prop->getter = 0; + prop->setter = 0; + ant_ic_epoch_bump(); + return true; +} diff --git a/src/silver/ast.c b/src/silver/ast.c index a98aecb..7b512b3 100644 --- a/src/silver/ast.c +++ b/src/silver/ast.c @@ -603,6 +603,7 @@ static sv_ast_t *parse_primary(P) { i++; } sv_ast_t *s = mk(N_STRING); + s->flags |= FN_TEMPLATE_SEGMENT; s->aux = (const char *)&in[seg_start]; s->aux_len = (uint32_t)(i - seg_start); sv_tpl_cooked_t cooked = decode_template_segment(p, in, seg_start, i); @@ -1912,7 +1913,7 @@ static sv_ast_t *parse_stmt(P) { l_break: { CONSUME(); sv_ast_t *n = mk(N_BREAK); - if (NEXT() == TOK_IDENTIFIER && !HAD_NEWLINE) { + if ((NEXT() == TOK_IDENTIFIER || is_contextual_ident_tok(TOK)) && !HAD_NEWLINE) { n->str = tok_ident_str(p, &n->len); CONSUME(); } @@ -1923,7 +1924,7 @@ static sv_ast_t *parse_stmt(P) { l_continue: { CONSUME(); sv_ast_t *n = mk(N_CONTINUE); - if (NEXT() == TOK_IDENTIFIER && !HAD_NEWLINE) { + if ((NEXT() == TOK_IDENTIFIER || is_contextual_ident_tok(TOK)) && !HAD_NEWLINE) { n->str = tok_ident_str(p, &n->len); CONSUME(); } @@ -2019,6 +2020,9 @@ static sv_ast_t *parse_stmt(P) { } l_import: { + uint8_t la = LA(); + if (la == TOK_LPAREN || la == TOK_DOT) + goto l_expr_stmt; CONSUME(); return parse_import_stmt(p); } @@ -2029,7 +2033,7 @@ static sv_ast_t *parse_stmt(P) { } l_expr_stmt: { - if (TOK == TOK_IDENTIFIER) { + if (TOK == TOK_IDENTIFIER || is_contextual_ident_tok(TOK)) { uint8_t la = LA(); if (la == TOK_COLON) { sv_ast_t *label = mk(N_LABEL); diff --git a/src/silver/compiler.c b/src/silver/compiler.c index d891ca3..1688901 100644 --- a/src/silver/compiler.c +++ b/src/silver/compiler.c @@ -65,6 +65,7 @@ typedef struct sv_compiler { sv_atom_t *atoms; int atom_count; int atom_cap; + int ic_count; sv_local_t *locals; int local_count; @@ -265,6 +266,59 @@ static int add_constant(sv_compiler_t *c, ant_value_t val) { return c->const_count++; } +static void build_gc_const_tables(sv_func_t *func) { + if (!func || func->const_count <= 0 || !func->constants) return; + + int child_count = 0; + for (int i = 0; i < func->const_count; i++) { + if (vtype(func->constants[i]) == T_CFUNC) child_count++; + } + + if (child_count > 0) { + func->child_funcs = code_arena_bump((size_t)child_count * sizeof(sv_func_t *)); + if (func->child_funcs) { + int out = 0; + for (int i = 0; i < func->const_count; i++) { + if (vtype(func->constants[i]) != T_CFUNC) continue; + func->child_funcs[out++] = (sv_func_t *)(uintptr_t)vdata(func->constants[i]); + } func->child_func_count = child_count; + } + } + + uint8_t *marked_slots = calloc((size_t)func->const_count, sizeof(uint8_t)); + if (!marked_slots) return; + + int slot_count = 0; + for (int pc = 0; pc < func->code_len;) { + sv_op_t op = (sv_op_t)func->code[pc]; + int size = (op < OP__COUNT) ? sv_op_size[op] : 0; + if (size <= 0) break; + + if (op == OP_PUT_CONST) { + uint32_t idx = sv_get_u32(func->code + pc + 1); + if (idx < (uint32_t)func->const_count && !marked_slots[idx]) { + marked_slots[idx] = 1; slot_count++; + } + } + + pc += size; + } + + if (slot_count > 0) { + func->gc_const_slots = code_arena_bump((size_t)slot_count * sizeof(uint32_t)); + if (func->gc_const_slots) { + int out = 0; + for (int i = 0; i < func->const_count; i++) { + if (!marked_slots[i]) continue; + func->gc_const_slots[out++] = (uint32_t)i; + } + func->gc_const_slot_count = slot_count; + } + } + + free(marked_slots); +} + static void emit_constant(sv_compiler_t *c, ant_value_t val) { int idx = add_constant(c, val); if (idx <= 255) { @@ -291,13 +345,17 @@ static inline bool is_quoted_ident_key(const sv_ast_t *node) { return ((open == '\'' && close == '\'') || (open == '"' && close == '"')); } +static inline bool is_template_segment(const sv_ast_t *node) { + return node && node->type == N_STRING && (node->flags & FN_TEMPLATE_SEGMENT); +} + static inline bool is_invalid_cooked_string(const sv_ast_t *node) { - return node && (node->flags & FN_INVALID_COOKED); + return is_template_segment(node) && (node->flags & FN_INVALID_COOKED); } static inline ant_value_t ast_string_const(sv_compiler_t *c, const sv_ast_t *node) { - if (!node || !node->str) return js_mkstr(c->js, "", 0); - return js_mkstr(c->js, node->str, node->len); + if (!node || !node->str) return js_mkstr_permanent(c->js, "", 0); + return js_mkstr_permanent(c->js, node->str, node->len); } static inline void compile_static_property_key(sv_compiler_t *c, sv_ast_t *key) { @@ -314,15 +372,15 @@ static inline void compile_static_property_key(sv_compiler_t *c, sv_ast_t *key) if (key->type == N_NUMBER) { char buf[32]; int n = snprintf(buf, sizeof(buf), "%g", key->num); - emit_constant(c, js_mkstr(c->js, buf, (size_t)n)); + emit_constant(c, js_mkstr_permanent(c->js, buf, (size_t)n)); return; } if (key->type == N_IDENT) { if (is_quoted_ident_key(key)) - emit_constant(c, js_mkstr(c->js, key->str + 1, key->len - 2)); + emit_constant(c, js_mkstr_permanent(c->js, key->str + 1, key->len - 2)); else - emit_constant(c, js_mkstr(c->js, key->str, key->len)); + emit_constant(c, js_mkstr_permanent(c->js, key->str, key->len)); return; } @@ -330,24 +388,71 @@ static inline void compile_static_property_key(sv_compiler_t *c, sv_ast_t *key) } static int add_atom(sv_compiler_t *c, const char *str, uint32_t len) { + const char *interned = intern_string(str, (size_t)len); + const char *stored = interned; + if (!stored) { + char *copy = code_arena_bump(len); + memcpy(copy, str, len); + stored = copy; + } + for (int i = 0; i < c->atom_count; i++) { - if (c->atoms[i].len == len && memcmp(c->atoms[i].str, str, len) == 0) + if (c->atoms[i].len == len && c->atoms[i].str == stored) return i; } if (c->atom_count >= c->atom_cap) { c->atom_cap = c->atom_cap ? c->atom_cap * 2 : 16; c->atoms = realloc(c->atoms, (size_t)c->atom_cap * sizeof(sv_atom_t)); } - char *copy = code_arena_bump(len); - memcpy(copy, str, len); - c->atoms[c->atom_count] = (sv_atom_t){ .str = copy, .len = len }; + c->atoms[c->atom_count] = (sv_atom_t){ .str = stored, .len = len }; return c->atom_count++; } +static inline bool sv_op_has_ic_slot(sv_op_t op) { + return op == OP_GET_GLOBAL || op == OP_GET_GLOBAL_UNDEF || + op == OP_GET_FIELD || op == OP_GET_FIELD2 || op == OP_PUT_FIELD; +} + +static uint16_t alloc_ic_idx(sv_compiler_t *c) { + if (!c || c->ic_count >= (int)UINT16_MAX) return UINT16_MAX; + return (uint16_t)c->ic_count++; +} + +static void sv_func_init_obj_sites(sv_func_t *func) { + if (!func || !func->code || func->code_len <= 0) return; + + uint32_t count = 0; + for (int pc = 0; pc < func->code_len; ) { + sv_op_t op = (sv_op_t)func->code[pc]; + if (op == OP_OBJECT) count++; + uint8_t size = (op < OP__COUNT) ? sv_op_size[op] : 1; + pc += (size > 0) ? size : 1; + } + if (count == 0) return; + + func->obj_sites = code_arena_bump((size_t)count * sizeof(sv_obj_site_cache_t)); + memset(func->obj_sites, 0, (size_t)count * sizeof(sv_obj_site_cache_t)); + func->obj_site_count = (uint16_t)count; + + uint32_t idx = 0; + for (int pc = 0; pc < func->code_len && idx < count; ) { + sv_op_t op = (sv_op_t)func->code[pc]; + if (op == OP_OBJECT) func->obj_sites[idx++].bc_off = (uint32_t)pc; + uint8_t size = (op < OP__COUNT) ? sv_op_size[op] : 1; + pc += (size > 0) ? size : 1; + } +} + +static void emit_atom_idx_op(sv_compiler_t *c, sv_op_t op, uint32_t atom_idx) { + emit_op(c, op); + emit_u32(c, atom_idx); + if (sv_op_has_ic_slot(op)) + emit_u16(c, alloc_ic_idx(c)); +} + static void emit_atom_op(sv_compiler_t *c, sv_op_t op, const char *str, uint32_t len) { int idx = add_atom(c, str, len); - emit_op(c, op); - emit_u32(c, (uint32_t)idx); + emit_atom_idx_op(c, op, (uint32_t)idx); } static inline void emit_set_function_name( @@ -469,7 +574,7 @@ static void mark_slot_type(sv_compiler_t *c, int slot, uint8_t type) { static void set_local_inferred_type(sv_compiler_t *c, int local_idx, uint8_t type) { if (local_idx < 0 || local_idx >= c->local_count) return; c->locals[local_idx].inferred_type = type; - if (c->locals[local_idx].depth == -1) return; /* params use arg slots */ + if (c->locals[local_idx].depth == -1) return; int slot = local_idx - c->param_locals; mark_slot_type(c, slot, type); } @@ -1338,7 +1443,7 @@ static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { int n = tpl->args.count; int n_strings = 0, n_exprs = 0; for (int i = 0; i < n; i++) { - if (tpl->args.items[i]->type == N_STRING) n_strings++; + if (is_template_segment(tpl->args.items[i])) n_strings++; else n_exprs++; } int cache_idx = add_constant(c, js_mkundef()); @@ -1348,7 +1453,7 @@ static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { emit_op(c, OP_POP); for (int i = 0; i < n; i++) { sv_ast_t *item = tpl->args.items[i]; - if (item->type != N_STRING) continue; + if (!is_template_segment(item)) continue; if (is_invalid_cooked_string(item)) emit_op(c, OP_UNDEF); else emit_constant(c, ast_string_const(c, item)); @@ -1357,10 +1462,10 @@ static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { emit_u16(c, (uint16_t)n_strings); for (int i = 0; i < n; i++) { sv_ast_t *item = tpl->args.items[i]; - if (item->type != N_STRING) continue; + if (!is_template_segment(item)) continue; const char *raw = item->aux ? item->aux : item->str; uint32_t raw_len = item->aux ? item->aux_len : item->len; - emit_constant(c, js_mkstr(c->js, raw ? raw : "", raw_len)); + emit_constant(c, js_mkstr_permanent(c->js, raw ? raw : "", raw_len)); } emit_op(c, OP_ARRAY); emit_u16(c, (uint16_t)n_strings); @@ -1381,7 +1486,7 @@ static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { patch_jump(c, skip_jump); for (int i = 0; i < n; i++) { sv_ast_t *item = tpl->args.items[i]; - if (item->type == N_STRING) continue; + if (is_template_segment(item)) continue; compile_expr(c, item); } emit_op(c, OP_CALL); @@ -1395,8 +1500,8 @@ static void compile_expr(sv_compiler_t *c, sv_ast_t *node) { break; case N_REGEXP: - emit_constant(c, js_mkstr(c->js, node->str ? node->str : "", node->len)); - emit_constant(c, js_mkstr(c->js, node->aux ? node->aux : "", node->aux_len)); + emit_constant(c, js_mkstr_permanent(c->js, node->str ? node->str : "", node->len)); + emit_constant(c, js_mkstr_permanent(c->js, node->aux ? node->aux : "", node->aux_len)); emit_op(c, OP_REGEXP); break; @@ -1438,7 +1543,7 @@ static void compile_binary(sv_compiler_t *c, sv_ast_t *node) { if (op == TOK_IN && node->left->type == N_IDENT && node->left->len > 0 && node->left->str[0] == '#') { - emit_constant(c, js_mkstr(c->js, node->left->str, node->left->len)); + emit_constant(c, js_mkstr_permanent(c->js, node->left->str, node->left->len)); compile_expr(c, node->right); emit_op(c, OP_IN); return; @@ -1483,7 +1588,10 @@ static void compile_binary(sv_compiler_t *c, sv_ast_t *node) { case TOK_SHL: emit_op(c, OP_SHL); break; case TOK_SHR: emit_op(c, OP_SHR); break; case TOK_ZSHR: emit_op(c, OP_USHR); break; - case TOK_INSTANCEOF: emit_op(c, OP_INSTANCEOF); break; + case TOK_INSTANCEOF: + emit_op(c, OP_INSTANCEOF); + emit_u16(c, alloc_ic_idx(c)); + break; case TOK_IN: emit_op(c, OP_IN); break; default: emit_op(c, OP_UNDEF); break; } @@ -1520,18 +1628,15 @@ static void compile_update(sv_compiler_t *c, sv_ast_t *node) { compile_expr(c, target->left); emit_op(c, OP_DUP); int atom = add_atom(c, target->right->str, target->right->len); - emit_op(c, OP_GET_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_GET_FIELD, (uint32_t)atom); if (prefix) { emit_op(c, is_inc ? OP_INC : OP_DEC); emit_op(c, OP_INSERT2); - emit_op(c, OP_PUT_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); } else { emit_op(c, is_inc ? OP_POST_INC : OP_POST_DEC); emit_op(c, OP_SWAP_UNDER); - emit_op(c, OP_PUT_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); } } else if (target->type == N_MEMBER && (target->flags & 1)) { compile_expr(c, target->left); @@ -1562,8 +1667,7 @@ static void compile_assign(sv_compiler_t *c, sv_ast_t *node) { compile_expr(c, target->left); compile_expr(c, node->right); emit_op(c, OP_INSERT2); - emit_op(c, OP_PUT_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); return; } @@ -1652,16 +1756,14 @@ static void compile_assign(sv_compiler_t *c, sv_ast_t *node) { op == TOK_NULLISH_ASSIGN) { compile_expr(c, target->left); emit_op(c, OP_DUP); - emit_op(c, OP_GET_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_GET_FIELD, (uint32_t)atom); int skip = emit_jump(c, op == TOK_LOR_ASSIGN ? OP_JMP_TRUE_PEEK : op == TOK_LAND_ASSIGN ? OP_JMP_FALSE_PEEK : OP_JMP_NOT_NULLISH); emit_op(c, OP_POP); compile_expr(c, node->right); emit_op(c, OP_INSERT2); - emit_op(c, OP_PUT_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); int end = emit_jump(c, OP_JMP); patch_jump(c, skip); emit_op(c, OP_NIP); @@ -1671,8 +1773,7 @@ static void compile_assign(sv_compiler_t *c, sv_ast_t *node) { compile_expr(c, target->left); emit_op(c, OP_DUP); - emit_op(c, OP_GET_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_GET_FIELD, (uint32_t)atom); compile_expr(c, node->right); switch (op) { case TOK_PLUS_ASSIGN: emit_op(c, OP_ADD); break; @@ -1690,8 +1791,7 @@ static void compile_assign(sv_compiler_t *c, sv_ast_t *node) { default: break; } emit_op(c, OP_INSERT2); - emit_op(c, OP_PUT_FIELD); - emit_u32(c, (uint32_t)atom); + emit_atom_idx_op(c, OP_PUT_FIELD, (uint32_t)atom); } else if (target->type == N_MEMBER && (target->flags & 1)) { if (op == TOK_LOR_ASSIGN || op == TOK_LAND_ASSIGN || @@ -1780,7 +1880,7 @@ static void compile_typeof(sv_compiler_t *c, sv_ast_t *node) { uint8_t inferred = get_local_inferred_type(c, local); const char *known = typeof_name_for_type(inferred); if (known) { - emit_constant(c, js_mkstr(c->js, known, strlen(known))); + emit_constant(c, js_mkstr_permanent(c->js, known, strlen(known))); return; } emit_get_var(c, arg->str, arg->len); @@ -1809,7 +1909,7 @@ static void compile_delete(sv_compiler_t *c, sv_ast_t *node) { sv_ast_t *arg = node->right; if (arg->type == N_MEMBER && !(arg->flags & 1)) { compile_expr(c, arg->left); - ant_value_t key = js_mkstr(c->js, arg->right->str, arg->right->len); + ant_value_t key = js_mkstr_permanent(c->js, arg->right->str, arg->right->len); emit_constant(c, key); emit_op(c, OP_DELETE); } else if (arg->type == N_MEMBER && (arg->flags & 1)) { @@ -1828,12 +1928,12 @@ static void compile_delete(sv_compiler_t *c, sv_ast_t *node) { static void compile_template(sv_compiler_t *c, sv_ast_t *node) { int n = node->args.count; if (n == 0) { - emit_constant(c, js_mkstr(c->js, "", 0)); + emit_constant(c, js_mkstr_permanent(c->js, "", 0)); return; } for (int i = 0; i < n; i++) { sv_ast_t *item = node->args.items[i]; - if (item->type == N_STRING && is_invalid_cooked_string(item)) { + if (is_invalid_cooked_string(item)) { static const char msg[] = "Invalid or unexpected token"; int atom = add_atom(c, msg, sizeof(msg) - 1); emit_op(c, OP_THROW_ERROR); @@ -1843,11 +1943,11 @@ static void compile_template(sv_compiler_t *c, sv_ast_t *node) { } } compile_expr(c, node->args.items[0]); - if (node->args.items[0]->type != N_STRING) + if (!is_template_segment(node->args.items[0])) emit_op(c, OP_TO_PROPKEY); for (int i = 1; i < n; i++) { compile_expr(c, node->args.items[i]); - if (node->args.items[i]->type != N_STRING) + if (!is_template_segment(node->args.items[i])) emit_op(c, OP_TO_PROPKEY); emit_op(c, OP_ADD); } @@ -1937,7 +2037,7 @@ static sv_call_kind_t compile_call_setup_non_optional(sv_compiler_t *c, sv_ast_t if (callee->flags & 1) compile_expr(c, callee->right); else - emit_constant(c, js_mkstr(c->js, callee->right->str, callee->right->len)); + emit_constant(c, js_mkstr_permanent(c->js, callee->right->str, callee->right->len)); emit_op(c, OP_GET_SUPER_VAL); return SV_CALL_METHOD; } @@ -1952,6 +2052,25 @@ static sv_call_kind_t compile_call_setup_non_optional(sv_compiler_t *c, sv_ast_t return SV_CALL_DIRECT; } +static bool compile_call_is_proto_intrinsic( + sv_compiler_t *c, sv_ast_t *node, bool has_spread +) { + if (!node || has_spread || node->args.count != 1) return false; + sv_ast_t *callee = node->left; + if (!callee || callee->type != N_MEMBER) return false; + if ((callee->flags & 1) || !callee->right || !callee->right->str) return false; + if (is_ident_name(callee->left, "super")) return false; + if (!is_ident_str(callee->right->str, callee->right->len, "isPrototypeOf", 13)) + return false; + + compile_expr(c, callee->left); + compile_receiver_property_get(c, callee); + compile_expr(c, node->args.items[0]); + emit_op(c, OP_CALL_IS_PROTO); + emit_u16(c, alloc_ic_idx(c)); + return true; +} + static void compile_optional_call_after_setup( sv_compiler_t *c, sv_ast_t *call_node, sv_call_kind_t kind, bool has_spread @@ -2020,6 +2139,9 @@ static void compile_call(sv_compiler_t *c, sv_ast_t *node) { return; } + if (compile_call_is_proto_intrinsic(c, node, has_spread)) + return; + if (!has_spread && is_ident_name(callee, "eval")) { if (node->args.count > 0) compile_expr(c, node->args.items[0]); @@ -2066,7 +2188,7 @@ static void compile_member(sv_compiler_t *c, sv_ast_t *node) { if (node->flags & 1) compile_expr(c, node->right); else - emit_constant(c, js_mkstr(c->js, node->right->str, node->right->len)); + emit_constant(c, js_mkstr_permanent(c->js, node->right->str, node->right->len)); emit_op(c, OP_GET_SUPER_VAL); return; } @@ -2255,7 +2377,7 @@ static void emit_build_object_rest(sv_compiler_t *c) { static void emit_delete_rest_key(sv_compiler_t *c, sv_ast_t *key) { emit_op(c, OP_DUP); if (key->type == N_IDENT) { - emit_constant(c, js_mkstr(c->js, key->str, key->len)); + emit_constant(c, js_mkstr_permanent(c->js, key->str, key->len)); } else if (key->type == N_STRING) { emit_constant(c, ast_string_const(c, key)); } else if (key->type == N_NUMBER) { @@ -3336,6 +3458,16 @@ static void compile_for_each(sv_compiler_t *c, sv_ast_t *node, bool is_for_of) { } +static void emit_close_upvals_to_depth(sv_compiler_t *c, int target_depth) { +for (int i = c->local_count - 1; i >= 0; i--) { + if (c->locals[i].depth <= target_depth) break; + if (c->locals[i].captured) { + int frame_slot = local_to_frame_slot(c, i); + emit_op(c, OP_CLOSE_UPVAL); + emit_u16(c, (uint16_t)frame_slot); + } +}} + static void compile_break(sv_compiler_t *c, sv_ast_t *node) { if (c->loop_count == 0) return; @@ -3351,6 +3483,7 @@ static void compile_break(sv_compiler_t *c, sv_ast_t *node) { } } + emit_close_upvals_to_depth(c, c->loops[target].scope_depth); int offset = emit_jump(c, OP_JMP); patch_list_add(&c->loops[target].breaks, offset); } @@ -3363,10 +3496,12 @@ static void compile_continue(sv_compiler_t *c, sv_ast_t *node) { c->loops[i].label && c->loops[i].label_len == node->len && memcmp(c->loops[i].label, node->str, node->len) == 0) { + emit_close_upvals_to_depth(c, c->loops[i].scope_depth); patch_list_add(&c->loops[i].continues, emit_jump(c, OP_JMP)); return; } } else if (!c->loops[i].is_switch) { + emit_close_upvals_to_depth(c, c->loops[i].scope_depth); patch_list_add(&c->loops[i].continues, emit_jump(c, OP_JMP)); return; } @@ -3714,16 +3849,23 @@ static void compile_class(sv_compiler_t *c, sv_ast_t *node) { fn->code = code_arena_bump((size_t)comp.code_len); memcpy(fn->code, comp.code, (size_t)comp.code_len); fn->code_len = comp.code_len; + sv_func_init_obj_sites(fn); if (comp.const_count > 0) { fn->constants = code_arena_bump((size_t)comp.const_count * sizeof(ant_value_t)); memcpy(fn->constants, comp.constants, (size_t)comp.const_count * sizeof(ant_value_t)); fn->const_count = comp.const_count; + build_gc_const_tables(fn); } if (comp.atom_count > 0) { fn->atoms = code_arena_bump((size_t)comp.atom_count * sizeof(sv_atom_t)); memcpy(fn->atoms, comp.atoms, (size_t)comp.atom_count * sizeof(sv_atom_t)); fn->atom_count = comp.atom_count; } + fn->ic_count = (uint16_t)comp.ic_count; + if (fn->ic_count > 0) { + fn->ic_slots = code_arena_bump((size_t)fn->ic_count * sizeof(sv_ic_entry_t)); + memset(fn->ic_slots, 0, (size_t)fn->ic_count * sizeof(sv_ic_entry_t)); + } if (comp.upvalue_count > 0) { fn->upval_descs = code_arena_bump( (size_t)comp.upvalue_count * sizeof(sv_upval_desc_t)); @@ -4159,11 +4301,13 @@ static sv_func_t *compile_function_body( func->code = code_arena_bump((size_t)comp.code_len); memcpy(func->code, comp.code, (size_t)comp.code_len); func->code_len = comp.code_len; + sv_func_init_obj_sites(func); if (comp.const_count > 0) { func->constants = code_arena_bump((size_t)comp.const_count * sizeof(ant_value_t)); memcpy(func->constants, comp.constants, (size_t)comp.const_count * sizeof(ant_value_t)); func->const_count = comp.const_count; + build_gc_const_tables(func); } if (comp.atom_count > 0) { @@ -4172,6 +4316,12 @@ static sv_func_t *compile_function_body( func->atom_count = comp.atom_count; } + func->ic_count = (uint16_t)comp.ic_count; + if (func->ic_count > 0) { + func->ic_slots = code_arena_bump((size_t)func->ic_count * sizeof(sv_ic_entry_t)); + memset(func->ic_slots, 0, (size_t)func->ic_count * sizeof(sv_ic_entry_t)); + } + if (comp.upvalue_count > 0) { func->upval_descs = code_arena_bump( (size_t)comp.upvalue_count * sizeof(sv_upval_desc_t)); @@ -4313,6 +4463,9 @@ void sv_disasm(ant_t *js, sv_func_t *func, const char *label) { fprintf(stderr, " [%.*s]", (int)func->atoms[idx].len, func->atoms[idx].str); else fprintf(stderr, " a%u", idx); + if ((op == OP_GET_GLOBAL || op == OP_GET_GLOBAL_UNDEF || + op == OP_GET_FIELD || op == OP_GET_FIELD2 || op == OP_PUT_FIELD) && size >= 7) + fprintf(stderr, " ic[%u]", sv_get_u16(func->code + pc + 5)); break; } case SVF_atom_u8: { @@ -4382,7 +4535,7 @@ void sv_disasm(ant_t *js, sv_func_t *func, const char *label) { if (t == T_STR) { ant_offset_t slen; ant_offset_t soff = vstr(js, v, &slen); - fprintf(stderr, " %d: \n", i, (int)slen, (int)slen, &js->mem[soff]); + fprintf(stderr, " %d: \n", i, (int)slen, (int)slen, (const char *)(uintptr_t)soff); } else if (t == T_NUM) { fprintf(stderr, " %d: \n", i, tod(v)); } else if (t == T_CFUNC) { diff --git a/src/silver/engine.c b/src/silver/engine.c index d006f01..15b1b90 100644 --- a/src/silver/engine.c +++ b/src/silver/engine.c @@ -1,9 +1,9 @@ -#include "silver/engine.h" -#include "silver/swarm.h" - -#include #include "gc.h" #include "errors.h" +#include + +#include "silver/engine.h" +#include "silver/swarm.h" #include "ops/literals.h" #include "ops/stack.h" @@ -174,42 +174,7 @@ void js_set_error_site_from_vm_top(ant_t *js) { js_set_error_site_from_bc(js, func, bc_off, func->filename); } -void sv_vm_gc_roots(sv_vm_t *vm, void (*op_val)(void *, ant_value_t *), void *ctx) { - if (!vm) return; - - for (int i = 0; i < vm->sp; i++) - op_val(ctx, &vm->stack[i]); - - for (int i = 0; i <= vm->fp; i++) { - op_val(ctx, &vm->frames[i].this); - op_val(ctx, &vm->frames[i].callee); - op_val(ctx, &vm->frames[i].new_target); - op_val(ctx, &vm->frames[i].super_val); - op_val(ctx, &vm->frames[i].with_obj); - } - - for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) { - if (uv->location != &uv->closed) continue; - if (uv->gc_epoch == gc_epoch_counter) continue; - op_val(ctx, &uv->closed); - } - for (int i = 0; i <= vm->fp; i++) { - sv_frame_t *frame = &vm->frames[i]; - for (int j = 0; j < frame->upvalue_count; j++) { - sv_upvalue_t *uv = frame->upvalues ? frame->upvalues[j] : NULL; - if (!uv) continue; - if (uv->location != &uv->closed) continue; - if (uv->gc_epoch == gc_epoch_counter) continue; - op_val(ctx, &uv->closed); - } - } - - for (int i = 0; i <= vm->fp; i++) { - if (vm->frames[i].completion.kind != SV_COMPLETION_NONE) - op_val(ctx, &vm->frames[i].completion.value); - } -} void sv_vm_visit_frame_funcs(sv_vm_t *vm, void (*visitor)(void *, sv_func_t *), void *ctx) { if (!vm) return; @@ -228,9 +193,6 @@ ant_value_t sv_execute_entry_tla(ant_t *js, sv_func_t *func, ant_value_t this_va return sv_start_tla(js, func, this_val); } -void sv_vm_gc_roots_pending(void (*op_val)(void *, ant_value_t *), void *ctx) { - sv_vm_gc_roots_async(op_val, ctx); -} static inline ant_value_t sv_stage_frame_args( sv_vm_t *vm, ant_t *js, sv_func_t *func, ant_value_t *args, int argc, @@ -341,6 +303,13 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant ? vm->jit_resume.n_locals : func->max_locals; for (int64_t i = 0; i < rl; i++) entry_lp[i] = vm->jit_resume.locals[i]; + + ant_value_t *old_lp = vm->jit_resume.locals; + for (sv_upvalue_t *uv = vm->open_upvalues; uv; uv = uv->next) { + if (uv->location >= old_lp && uv->location < old_lp + rl) { + ptrdiff_t slot = uv->location - old_lp; + uv->location = &entry_lp[slot]; + }} } #endif frame->bp = entry_bp; @@ -456,7 +425,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_FALSE: { sv_op_false(vm); NEXT(1); } L_THIS: { sv_op_this(vm, frame); NEXT(1); } L_GLOBAL: { sv_op_global(vm, js); NEXT(1); } - L_OBJECT: { sv_op_object(vm, js); NEXT(1); } + L_OBJECT: { sv_op_object(vm, js, func, ip); NEXT(1); } L_ARRAY: { sv_op_array(vm, js, ip); NEXT(3); } L_REGEXP: { sv_op_regexp(vm, js); NEXT(1); } @@ -477,11 +446,11 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_ROT4_UNDER: { sv_op_rot4_under(vm); NEXT(1); } L_GET_LOCAL: { sv_op_get_local(vm, lp, ip); NEXT(3); } - L_PUT_LOCAL: { sv_op_put_local(vm, lp, ip); NEXT(3); } - L_SET_LOCAL: { sv_op_set_local(vm, lp, ip); NEXT(3); } + L_PUT_LOCAL: { sv_op_put_local(vm, lp, func, ip); NEXT(3); } + L_SET_LOCAL: { sv_op_set_local(vm, lp, func, ip); NEXT(3); } L_GET_LOCAL8: { sv_op_get_local8(vm, lp, ip); NEXT(2); } - L_PUT_LOCAL8: { sv_op_put_local8(vm, lp, ip); NEXT(2); } - L_SET_LOCAL8: { sv_op_set_local8(vm, lp, ip); NEXT(2); } + L_PUT_LOCAL8: { sv_op_put_local8(vm, lp, func, ip); NEXT(2); } + L_SET_LOCAL8: { sv_op_set_local8(vm, lp, func, ip); NEXT(2); } L_SET_LOCAL_UNDEF: { sv_op_set_local_undef(lp, ip); NEXT(3); } L_GET_LOCAL_CHK: { VM_CHECK(sv_op_get_local_chk(vm, lp, js, func, ip)); NEXT(7); } @@ -497,13 +466,13 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_SET_UPVAL: { sv_op_set_upval(vm, frame, ip); NEXT(3); } L_CLOSE_UPVAL: { sv_op_close_upval(vm, frame, ip); NEXT(3); } - L_GET_GLOBAL: { VM_CHECK(sv_op_get_global(vm, js, func, ip)); NEXT(5); } - L_GET_GLOBAL_UNDEF: { sv_op_get_global_undef(vm, js, func, ip); NEXT(5); } + L_GET_GLOBAL: { VM_CHECK(sv_op_get_global(vm, js, func, ip)); NEXT(7); } + L_GET_GLOBAL_UNDEF: { sv_op_get_global_undef(vm, js, func, ip); NEXT(7); } L_PUT_GLOBAL: { VM_CHECK(sv_op_put_global(vm, js, frame, func, ip)); NEXT(5); } - L_GET_FIELD: { VM_CHECK(sv_op_get_field(vm, js, func, ip)); NEXT(5); } - L_GET_FIELD2: { VM_CHECK(sv_op_get_field2(vm, js, func, ip)); NEXT(5); } - L_PUT_FIELD: { VM_CHECK(sv_op_put_field(vm, js, func, ip)); NEXT(5); } + L_GET_FIELD: { VM_CHECK(sv_op_get_field(vm, js, func, ip)); NEXT(7); } + L_GET_FIELD2: { VM_CHECK(sv_op_get_field2(vm, js, func, ip)); NEXT(7); } + L_PUT_FIELD: { VM_CHECK(sv_op_put_field(vm, js, func, ip)); NEXT(7); } L_GET_ELEM: { VM_CHECK(sv_op_get_elem(vm, js, func, ip)); NEXT(1); } L_GET_ELEM2: { VM_CHECK(sv_op_get_elem2(vm, js, func, ip)); NEXT(1); } L_PUT_ELEM: { VM_CHECK(sv_op_put_elem(vm, js)); NEXT(1); } @@ -571,7 +540,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_DIV: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_div(vm, js)); NEXT(1); } L_MOD: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_mod(vm, js)); NEXT(1); } L_NEG: { sv_tfb_record1(func, ip, vm->stack[vm->sp-1]); VM_CHECK(sv_op_neg(vm, js)); NEXT(1); } - L_ADD_LOCAL: { sv_tfb_record2(func, ip, lp[sv_get_u8(ip+1)], vm->stack[vm->sp-1]); VM_CHECK(sv_op_add_local(vm, lp, js, ip)); NEXT(2); } + L_ADD_LOCAL: { sv_tfb_record2(func, ip, lp[sv_get_u8(ip+1)], vm->stack[vm->sp-1]); VM_CHECK(sv_op_add_local(vm, lp, js, func, ip)); NEXT(2); } L_EXP: { VM_CHECK(sv_op_exp(vm, js)); NEXT(1); } L_UPLUS: { VM_CHECK(sv_op_uplus(vm, js)); NEXT(1); } @@ -579,8 +548,8 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_DEC: { sv_op_dec(vm); NEXT(1); } L_POST_INC: { sv_op_post_inc(vm); NEXT(1); } L_POST_DEC: { sv_op_post_dec(vm); NEXT(1); } - L_INC_LOCAL: { sv_op_inc_local(vm, lp, ip); NEXT(2); } - L_DEC_LOCAL: { sv_op_dec_local(vm, lp, ip); NEXT(2); } + L_INC_LOCAL: { sv_op_inc_local(vm, lp, func, ip); NEXT(2); } + L_DEC_LOCAL: { sv_op_dec_local(vm, lp, func, ip); NEXT(2); } L_EQ: { sv_op_eq(vm, js); NEXT(1); } L_NE: { sv_op_ne(vm, js); NEXT(1); } @@ -600,7 +569,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_GT: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_gt(vm, js)); NEXT(1); } L_GE: { sv_tfb_record2(func, ip, vm->stack[vm->sp-2], vm->stack[vm->sp-1]); VM_CHECK(sv_op_ge(vm, js)); NEXT(1); } - L_INSTANCEOF: { VM_CHECK(sv_op_instanceof(vm, js)); NEXT(1); } + L_INSTANCEOF: { VM_CHECK(sv_op_instanceof(vm, js, func, ip)); NEXT(3); } L_IN: { VM_CHECK(sv_op_in(vm, js)); NEXT(1); } L_IS_NULLISH: { sv_op_is_nullish(vm); NEXT(1); } L_IS_UNDEF_OR_NULL: { sv_op_is_undef_or_null(vm); NEXT(1); } @@ -619,13 +588,13 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_DELETE: { VM_CHECK(sv_op_delete(vm, js)); NEXT(1); } L_DELETE_VAR: { sv_op_delete_var(vm, js, func, ip); NEXT(5); } - L_JMP: { + L_JMP: { uint8_t *prev = ip; ip = sv_op_jmp(ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + gc_maybe(js); JIT_OSR_BACK_EDGE(); } - DISPATCH(); + DISPATCH(); } L_JMP_FALSE: { @@ -641,7 +610,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant : ip + sv_op_size[OP_JMP_FALSE] + sv_get_i32(ip + 1); } if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -660,7 +629,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant : ip + sv_op_size[OP_JMP_TRUE]; } if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -669,7 +638,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP_FALSE_PEEK: { uint8_t *prev = ip; ip = sv_op_jmp_false_peek(vm, js, ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -678,7 +647,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP_TRUE_PEEK: { uint8_t *prev = ip; ip = sv_op_jmp_true_peek(vm, js, ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -687,7 +656,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP_NOT_NULLISH: { uint8_t *prev = ip; ip = sv_op_jmp_not_nullish(vm, ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -696,7 +665,7 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP8: { uint8_t *prev = ip; ip = sv_op_jmp8(ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); + js->prop_refs_len = 0; JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -705,7 +674,6 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP_FALSE8: { uint8_t *prev = ip; ip = sv_op_jmp_false8(vm, js, ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -714,7 +682,6 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant L_JMP_TRUE8: { uint8_t *prev = ip; ip = sv_op_jmp_true8(vm, js, ip); if (ip <= prev) { - if (js->needs_gc) js_gc_maybe(js); JIT_OSR_BACK_EDGE(); } DISPATCH(); @@ -739,6 +706,8 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant #ifdef ANT_JIT { sv_func_t *callee = closure->func; + if (func->type_feedback) + sv_tfb_record_call_target(func, (int)(ip - func->code), callee); if (callee->jit_code) { ant_value_t jit_this = ( callee->is_arrow || vtype(closure->bound_this) != T_UNDEF) @@ -967,6 +936,29 @@ ant_value_t sv_execute_frame(sv_vm_t *vm, sv_func_t *func, ant_value_t this, ant NEXT(3); } + L_CALL_IS_PROTO: { + ant_value_t call_arg = vm->stack[vm->sp - 1]; + ant_value_t call_func = vm->stack[vm->sp - 2]; + ant_value_t call_this = vm->stack[vm->sp - 3]; + ant_value_t call_result; + + if ( + vtype(call_func) == T_CFUNC && + js_as_cfunc(call_func) == builtin_object_isPrototypeOf + ) { + call_result = sv_isproto_ic_eval(js, call_this, call_arg, func, ip); + } else { + ant_value_t call_args[1] = { call_arg }; + frame->ip = ip; + call_result = sv_vm_call(vm, js, call_func, call_this, call_args, 1, NULL, false); + } + + vm->sp -= 3; + if (is_err(call_result)) { sv_err = call_result; goto sv_throw; } + vm->stack[vm->sp++] = call_result; + NEXT(3); + } + L_TAIL_CALL: { uint16_t call_argc = sv_get_u16(ip + 1); ant_value_t call_func = vm->stack[vm->sp - call_argc - 1]; diff --git a/src/silver/glue.c b/src/silver/glue.c index b6138d7..6e7e69b 100644 --- a/src/silver/glue.c +++ b/src/silver/glue.c @@ -11,6 +11,7 @@ #include "ops/property.h" #include "ops/upvalues.h" #include "ops/comparison.h" +#include "ops/calls.h" bool jit_helper_stack_overflow(ant_t *js) { volatile char marker; @@ -68,8 +69,37 @@ ant_value_t jit_helper_call( return sv_vm_call(vm, js, func, this_val, args, argc, NULL, false); } -ant_value_t jit_helper_get_global(ant_t *js, const char *str, uint32_t len) { - return sv_global_get(js, str, len); +ant_value_t jit_helper_apply( + sv_vm_t *vm, ant_t *js, + ant_value_t func, ant_value_t this_val, + ant_value_t *args, int argc +) { + sv_call_args_t call; + sv_call_args_reset(&call, args, argc); + ant_value_t norm = sv_apply_normalize_args(js, &call); + if (is_err(norm)) return norm; + ant_value_t result = sv_vm_call(vm, js, func, this_val, call.args, call.argc, NULL, false); + sv_call_args_release(&call); + return result; +} + +ant_value_t jit_helper_rest( + sv_vm_t *vm, ant_t *js, + ant_value_t *args, int argc, int start +) { + ant_value_t arr = js_mkarr(js); + for (int i = start; i < argc; i++) + js_arr_push(js, arr, args[i]); + return arr; +} + +ant_value_t jit_helper_get_global( + ant_t *js, const char *str, + sv_func_t *func, int32_t bc_off +) { + uint8_t *ip = NULL; + if (func && bc_off >= 0 && bc_off < func->code_len) ip = func->code + bc_off; + return sv_global_get_interned_ic(js, str, func, ip); } ant_value_t jit_helper_seq(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) { @@ -101,8 +131,32 @@ ant_value_t jit_helper_not(sv_vm_t *vm, ant_t *js, ant_value_t v) { return mkval(T_BOOL, !js_truthy(js, v)); } -ant_value_t jit_helper_instanceof(sv_vm_t *vm, ant_t *js, ant_value_t l, ant_value_t r) { - return do_instanceof(js, l, r); +ant_value_t jit_helper_instanceof( + sv_vm_t *vm, ant_t *js, + ant_value_t l, ant_value_t r, + sv_func_t *func, int32_t bc_off +) { + (void)vm; + uint8_t *ip = NULL; + if (func && bc_off >= 0 && bc_off < func->code_len) ip = func->code + bc_off; + return sv_instanceof_ic_eval(js, l, r, func, ip); +} + +ant_value_t jit_helper_call_is_proto( + sv_vm_t *vm, ant_t *js, + ant_value_t call_this, ant_value_t call_func, ant_value_t arg, + sv_func_t *func, int32_t bc_off +) { + uint8_t *ip = NULL; + if (func && bc_off >= 0 && bc_off < func->code_len) ip = func->code + bc_off; + if ( + vtype(call_func) == T_CFUNC && + js_as_cfunc(call_func) == builtin_object_isPrototypeOf + ) { + return sv_isproto_ic_eval(js, call_this, arg, func, ip); + } + ant_value_t args[1] = { arg }; + return sv_vm_call(vm, js, call_func, call_this, args, 1, NULL, false); } // TODO: dont bail out @@ -128,9 +182,14 @@ ant_value_t jit_helper_get_field( sv_vm_t *vm, ant_t *js, ant_value_t obj, const char *str, uint32_t len, sv_func_t *func, int32_t bc_off ) { - if (vtype(obj) == T_NULL || vtype(obj) == T_UNDEF) + (void)vm; + uint8_t *ip = NULL; + if (func && bc_off >= 0 && bc_off < func->code_len) ip = func->code + bc_off; + sv_atom_t atom = { .str = str, .len = len }; + ant_value_t out = sv_prop_get_field_ic(js, obj, &atom, func, ip); + if ((vtype(obj) == T_NULL || vtype(obj) == T_UNDEF) && is_err(out)) jit_set_error_site_from_func(js, func, bc_off); - return sv_prop_get(js, obj, str, len); + return out; } ant_value_t jit_helper_to_propkey(sv_vm_t *vm, ant_t *js, ant_value_t v) { @@ -152,9 +211,9 @@ ant_value_t jit_helper_closure( sv_func_t *parent_func = parent_closure->func; sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(parent_func->constants[const_idx]); - sv_closure_t *closure = calloc(1, sizeof(sv_closure_t)); + sv_closure_t *closure = js_closure_alloc(js); if (!closure) return mkval(T_ERR, 0); - + closure->func = child; closure->bound_this = child->is_arrow ? this_val : js_mkundef(); closure->bound_args = js_mkundef(); @@ -169,7 +228,7 @@ ant_value_t jit_helper_closure( if (desc->is_local) { int idx = (int)desc->index; if (idx < parent_func->param_count) { - sv_upvalue_t *uv = calloc(1, sizeof(sv_upvalue_t)); + sv_upvalue_t *uv = js_upvalue_alloc(); uv->closed = (idx < argc && args) ? args[idx] : js_mkundef(); uv->location = &uv->closed; closure->upvalues[i] = uv; @@ -178,7 +237,7 @@ ant_value_t jit_helper_closure( if (li >= 0 && li < n_locals && locals) { closure->upvalues[i] = sv_capture_upvalue(vm, &locals[li]); } else { - sv_upvalue_t *uv = calloc(1, sizeof(sv_upvalue_t)); + sv_upvalue_t *uv = js_upvalue_alloc(); uv->closed = js_mkundef(); uv->location = &uv->closed; closure->upvalues[i] = uv; @@ -190,6 +249,7 @@ ant_value_t jit_helper_closure( ant_value_t func_obj = mkobj(js, 0); closure->func_obj = func_obj; + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method); js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); @@ -198,17 +258,15 @@ ant_value_t jit_helper_closure( sv_setup_function_prototype(js, func_obj, func_val); if (child->is_strict) - js_set_slot(js, func_obj, SLOT_STRICT, js_true); + js_set_slot(func_obj, SLOT_STRICT, js_true); if (child->is_async) { - js_set_slot(js, func_obj, SLOT_ASYNC, js_true); - ant_value_t async_proto = js_get_slot(js, js->global, SLOT_ASYNC_PROTO); - if (vtype(async_proto) == T_FUNC) - js_set_proto(js, func_obj, async_proto); + js_set_slot(func_obj, SLOT_ASYNC, js_true); + ant_value_t async_proto = js_get_slot(js->global, SLOT_ASYNC_PROTO); + if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); } else { - ant_value_t func_proto = js_get_slot(js, js->global, SLOT_FUNC_PROTO); - if (vtype(func_proto) == T_FUNC) - js_set_proto(js, func_obj, func_proto); + ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); + if (vtype(func_proto) == T_FUNC) js_set_proto_init(func_obj, func_proto); } return func_val; @@ -259,7 +317,11 @@ ant_value_t jit_helper_bailout_resume( void jit_helper_define_field( sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_value_t val, const char *str, uint32_t len -) { js_define_own_prop(js, obj, str, len, val); } +) { + (void)vm; + if (!sv_try_define_field_fast(js, obj, str, val)) + js_define_own_prop(js, obj, str, len, val); +} void jit_helper_set_name( @@ -276,7 +338,7 @@ ant_value_t jit_helper_get_length(sv_vm_t *vm, ant_t *js, ant_value_t obj) { if (vtype(obj) == T_STR) { ant_offset_t byte_len = 0; ant_offset_t off = vstr(js, obj, &byte_len); - const char *str_data = (const char *)&js->mem[off]; + const char *str_data = (const char *)(uintptr_t)(off); return tov((double)(uint32_t)utf16_strlen(str_data, byte_len)); } return js_getprop_fallback(js, obj, "length"); @@ -333,17 +395,14 @@ ant_value_t jit_helper_put_global( ant_value_t jit_helper_object(sv_vm_t *vm, ant_t *js) { ant_value_t obj = mkobj(js, 0); ant_value_t proto = js_get_ctor_proto(js, "Object", 6); - if (vtype(proto) == T_OBJ) js_set_proto(js, obj, proto); + if (vtype(proto) == T_OBJ) js_set_proto_init(obj, proto); return obj; } ant_value_t jit_helper_array(sv_vm_t *vm, ant_t *js, ant_value_t *elements, int count) { ant_value_t arr = js_mkarr(js); - ant_handle_t h = js_root(js, arr); for (int i = 0; i < count; i++) - js_arr_push(js, js_deref(js, h), elements[i]); - arr = js_deref(js, h); - js_unroot(js, h); + js_arr_push(js, arr, elements[i]); return arr; } @@ -377,8 +436,7 @@ ant_value_t jit_helper_get_elem2(sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_va ant_value_t jit_helper_set_proto(sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_value_t proto) { uint8_t pt = vtype(proto); - if (pt == T_OBJ || pt == T_NULL || pt == T_FUNC || pt == T_ARR) - js_set_proto(js, obj, proto); + if (pt == T_OBJ || pt == T_NULL || pt == T_FUNC || pt == T_ARR) js_set_proto_wb(js, obj, proto); return js_mkundef(); } @@ -451,7 +509,7 @@ ant_value_t jit_helper_delete(sv_vm_t *vm, ant_t *js, ant_value_t obj, ant_value if (!is_err(key_str) && vtype(key_str) == T_STR) { ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); return js_delete_prop(js, obj, kptr, klen); } return mkval(T_BOOL, 0); @@ -462,27 +520,37 @@ ant_value_t jit_helper_new( ant_value_t func, ant_value_t new_target, ant_value_t *args, int argc ) { + ant_value_t record_func = func; js->new_target = new_target; - if (vtype(func) == T_OBJ && is_proxy(js, func)) + if (vtype(func) == T_OBJ && is_proxy(func)) return js_proxy_construct(js, func, args, argc, new_target); + if (!js_is_constructor(js, func)) + return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); - ant_value_t obj = mkobj(js, 0); - + ant_value_t proto = js_mkundef(); if (vtype(func) == T_FUNC) { ant_value_t proto_source = func; ant_value_t func_obj = js_func_obj(func); - ant_value_t target_func = js_get_slot(js, func_obj, SLOT_TARGET_FUNC); - if (vtype(target_func) == T_FUNC) proto_source = target_func; - ant_value_t proto = js_getprop_fallback(js, proto_source, "prototype"); - if (is_object_type(proto)) js_set_proto(js, obj, proto); + ant_value_t target_func = js_get_slot(func_obj, SLOT_TARGET_FUNC); + if (vtype(target_func) == T_FUNC) { + proto_source = target_func; + record_func = target_func; + } + proto = js_getprop_fallback(js, proto_source, "prototype"); } + ant_value_t obj = js_mkobj_with_inobj_limit(js, sv_tfb_ctor_inobj_limit(record_func)); + if (is_object_type(proto)) js_set_proto_init(obj, proto); ant_value_t ctor_this = obj; ant_value_t result = sv_vm_call(vm, js, func, obj, args, argc, &ctor_this, true); - + if (is_err(result)) return result; - return is_object_type(result) ? result : (is_object_type(ctor_this) ? ctor_this : obj); + ant_value_t final_obj = + is_object_type(result) ? result + : (is_object_type(ctor_this) ? ctor_this : obj); + sv_tfb_record_ctor_prop_count(record_func, final_obj); + return final_obj; } #endif diff --git a/src/silver/ops/arithmetic.h b/src/silver/ops/arithmetic.h index 33b960e..6a5c928 100644 --- a/src/silver/ops/arithmetic.h +++ b/src/silver/ops/arithmetic.h @@ -28,9 +28,7 @@ static inline ant_value_t sv_op_add(sv_vm_t *vm, ant_t *js) { if (is_non_numeric(lu) || is_non_numeric(ru)) { ant_value_t l_str = coerce_to_str_concat(js, l); if (is_err(l_str)) return l_str; - ant_handle_t lh = js_root(js, l_str); ant_value_t r_str = coerce_to_str_concat(js, r); - l_str = js_deref(js, lh); js_unroot(js, lh); if (is_err(r_str)) return r_str; ant_value_t res = do_string_op(js, TOK_PLUS, l_str, r_str); vm->stack[vm->sp++] = res; @@ -189,25 +187,28 @@ static inline void sv_op_post_dec(sv_vm_t *vm) { vm->stack[vm->sp++] = tov(tod(old) - 1.0); } -static inline void sv_op_inc_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_inc_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint8_t idx = sv_get_u8(ip + 1); ant_value_t *slot = &lp[idx]; *slot = tov(tod(*slot) + 1.0); + sv_tfb_record_local(func, (int)idx, *slot); } -static inline void sv_op_dec_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_dec_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint8_t idx = sv_get_u8(ip + 1); ant_value_t *slot = &lp[idx]; *slot = tov(tod(*slot) - 1.0); + sv_tfb_record_local(func, (int)idx, *slot); (void)vm; } -static inline ant_value_t sv_op_add_local(sv_vm_t *vm, ant_value_t *lp, ant_t *js, uint8_t *ip) { +static inline ant_value_t sv_op_add_local(sv_vm_t *vm, ant_value_t *lp, ant_t *js, sv_func_t *func, uint8_t *ip) { uint8_t idx = sv_get_u8(ip + 1); ant_value_t *slot = &lp[idx]; ant_value_t val = vm->stack[--vm->sp]; if (vtype(*slot) == T_NUM && vtype(val) == T_NUM) { *slot = tov(tod(*slot) + tod(val)); + sv_tfb_record_local(func, (int)idx, *slot); return tov(0); } vm->stack[vm->sp++] = *slot; @@ -215,6 +216,7 @@ static inline ant_value_t sv_op_add_local(sv_vm_t *vm, ant_value_t *lp, ant_t *j ant_value_t err = sv_op_add(vm, js); if (is_err(err)) return err; *slot = vm->stack[--vm->sp]; + sv_tfb_record_local(func, (int)idx, *slot); return tov(0); } diff --git a/src/silver/ops/async.h b/src/silver/ops/async.h index fffeb5f..d729ab9 100644 --- a/src/silver/ops/async.h +++ b/src/silver/ops/async.h @@ -163,7 +163,6 @@ static inline ant_value_t sv_start_tla(ant_t *js, sv_func_t *func, ant_value_t t if (mco_status(mco) == MCO_DEAD) { remove_coroutine(coro); free_coroutine(coro); - js->needs_gc = true; } return promise; @@ -270,7 +269,6 @@ static inline ant_value_t sv_start_async_closure( if (mco_status(mco) == MCO_DEAD) { remove_coroutine(coro); free_coroutine(coro); - js->needs_gc = true; } return promise; @@ -295,12 +293,12 @@ static inline ant_value_t sv_await_value(ant_t *js, ant_value_t value) { coro->is_ready = false; ant_value_t resume_obj = mkobj(js, 0); - js_set_slot(js, resume_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); - js_set_slot(js, resume_obj, SLOT_CFUNC, js_mkfun(resume_coroutine_wrapper)); + js_set_slot(resume_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); + js_set_slot(resume_obj, SLOT_CFUNC, js_mkfun(resume_coroutine_wrapper)); ant_value_t reject_obj = mkobj(js, 0); - js_set_slot(js, reject_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); - js_set_slot(js, reject_obj, SLOT_CFUNC, js_mkfun(reject_coroutine_wrapper)); + js_set_slot(reject_obj, SLOT_CORO, tov((double)(uintptr_t)coro)); + js_set_slot(reject_obj, SLOT_CFUNC, js_mkfun(reject_coroutine_wrapper)); ant_value_t then_fn = js_getprop_fallback(js, value, "then"); if (vtype(then_fn) == T_FUNC || vtype(then_fn) == T_CFUNC) { @@ -329,9 +327,5 @@ static inline ant_value_t sv_await_value(ant_t *js, ant_value_t value) { return result; } -static inline void sv_vm_gc_roots_async(void (*op_val)(void *, ant_value_t *), void *ctx) { - for (coroutine_t *c = pending_coroutines.head; c; c = c->next) - if (c->sv_vm) sv_vm_gc_roots(c->sv_vm, op_val, ctx); -} #endif diff --git a/src/silver/ops/bitwise.h b/src/silver/ops/bitwise.h index 6a83d53..886fdba 100644 --- a/src/silver/ops/bitwise.h +++ b/src/silver/ops/bitwise.h @@ -8,9 +8,9 @@ static inline ant_value_t sv_op_band(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); ant_value_t res = bigint_bitand(js, l, r); if (is_err(res)) return res; @@ -18,7 +18,7 @@ static inline ant_value_t sv_op_band(sv_vm_t *vm, ant_t *js) { return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); - int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); + int32_t bi = (rty == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai & bi)); return tov(0); } @@ -26,9 +26,9 @@ static inline ant_value_t sv_op_band(sv_vm_t *vm, ant_t *js) { static inline ant_value_t sv_op_bor(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); ant_value_t res = bigint_bitor(js, l, r); if (is_err(res)) return res; @@ -36,7 +36,7 @@ static inline ant_value_t sv_op_bor(sv_vm_t *vm, ant_t *js) { return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); - int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); + int32_t bi = (rty == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai | bi)); return tov(0); } @@ -44,9 +44,9 @@ static inline ant_value_t sv_op_bor(sv_vm_t *vm, ant_t *js) { static inline ant_value_t sv_op_bxor(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); ant_value_t res = bigint_bitxor(js, l, r); if (is_err(res)) return res; @@ -54,7 +54,7 @@ static inline ant_value_t sv_op_bxor(sv_vm_t *vm, ant_t *js) { return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); - int32_t bi = (rt == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); + int32_t bi = (rty == T_NUM) ? js_to_int32(tod(r)) : js_to_int32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai ^ bi)); return tov(0); } @@ -74,9 +74,9 @@ static inline ant_value_t sv_op_bnot(sv_vm_t *vm, ant_t *js) { static inline ant_value_t sv_op_shl(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); uint64_t shift = 0; ant_value_t err = bigint_asint_bits(js, r, &shift); @@ -87,7 +87,7 @@ static inline ant_value_t sv_op_shl(sv_vm_t *vm, ant_t *js) { return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); - uint32_t bi = (rt == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); + uint32_t bi = (rty == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai << (bi & 0x1f))); return tov(0); } @@ -95,9 +95,9 @@ static inline ant_value_t sv_op_shl(sv_vm_t *vm, ant_t *js) { static inline ant_value_t sv_op_shr(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); uint64_t shift = 0; ant_value_t err = bigint_asint_bits(js, r, &shift); @@ -108,7 +108,7 @@ static inline ant_value_t sv_op_shr(sv_vm_t *vm, ant_t *js) { return tov(0); } int32_t ai = (lt == T_NUM) ? js_to_int32(tod(l)) : js_to_int32(js_to_number(js, l)); - uint32_t bi = (rt == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); + uint32_t bi = (rty == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai >> (bi & 0x1f))); return tov(0); } @@ -116,9 +116,9 @@ static inline ant_value_t sv_op_shr(sv_vm_t *vm, ant_t *js) { static inline ant_value_t sv_op_ushr(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_BIGINT || rt == T_BIGINT) { - if (lt != T_BIGINT || rt != T_BIGINT) + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_BIGINT || rty == T_BIGINT) { + if (lt != T_BIGINT || rty != T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); uint64_t shift = 0; ant_value_t err = bigint_asint_bits(js, r, &shift); @@ -129,7 +129,7 @@ static inline ant_value_t sv_op_ushr(sv_vm_t *vm, ant_t *js) { return tov(0); } uint32_t ai = (lt == T_NUM) ? js_to_uint32(tod(l)) : js_to_uint32(js_to_number(js, l)); - uint32_t bi = (rt == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); + uint32_t bi = (rty == T_NUM) ? js_to_uint32(tod(r)) : js_to_uint32(js_to_number(js, r)); vm->stack[vm->sp++] = tov((double)(ai >> (bi & 0x1f))); return tov(0); } diff --git a/src/silver/ops/calls.h b/src/silver/ops/calls.h index 52cee24..f4a0258 100644 --- a/src/silver/ops/calls.h +++ b/src/silver/ops/calls.h @@ -60,35 +60,44 @@ static inline ant_value_t sv_op_new(sv_vm_t *vm, ant_t *js, uint8_t *ip) { ant_value_t *args = &vm->stack[vm->sp - argc]; ant_value_t new_target = vm->stack[vm->sp - argc - 1]; ant_value_t func = vm->stack[vm->sp - argc - 2]; + ant_value_t record_func = func; js->new_target = new_target; - if (vtype(func) == T_OBJ && is_proxy(js, func)) { + if (vtype(func) == T_OBJ && is_proxy(func)) { ant_value_t result = js_proxy_construct(js, func, args, argc, new_target); vm->sp -= argc + 2; if (is_err(result)) return result; vm->stack[vm->sp++] = result; return result; } + if (!js_is_constructor(js, func)) { + vm->sp -= argc + 2; + return js_mkerr_typed(js, JS_ERR_TYPE, "not a constructor"); + } - ant_value_t obj = mkobj(js, 0); - + ant_value_t proto = js_mkundef(); if (vtype(func) == T_FUNC) { ant_value_t proto_source = func; ant_value_t func_obj = js_func_obj(func); - ant_value_t target_func = js_get_slot(js, func_obj, SLOT_TARGET_FUNC); - if (vtype(target_func) == T_FUNC) + ant_value_t target_func = js_get_slot(func_obj, SLOT_TARGET_FUNC); + if (vtype(target_func) == T_FUNC) { proto_source = target_func; - ant_value_t proto = js_getprop_fallback(js, proto_source, "prototype"); - if (is_object_type(proto)) js_set_proto(js, obj, proto); + record_func = target_func; + } + proto = js_getprop_fallback(js, proto_source, "prototype"); } + ant_value_t obj = js_mkobj_with_inobj_limit(js, sv_tfb_ctor_inobj_limit(record_func)); + if (is_object_type(proto)) js_set_proto_init(obj, proto); ant_value_t ctor_this = obj; ant_value_t result = sv_vm_call(vm, js, func, obj, args, argc, &ctor_this, true); vm->sp -= argc + 2; if (is_err(result)) return result; - vm->stack[vm->sp++] = + ant_value_t final_obj = is_object_type(result) ? result : (is_object_type(ctor_this) ? ctor_this : obj); + sv_tfb_record_ctor_prop_count(record_func, final_obj); + vm->stack[vm->sp++] = final_obj; return result; } @@ -118,7 +127,7 @@ static inline ant_value_t sv_op_eval(sv_vm_t *vm, ant_t *js, sv_frame_t *frame, } ant_offset_t len; ant_offset_t off = vstr(js, code, &len); - const char *str = (const char *)&js->mem[off]; + const char *str = (const char *)(uintptr_t)(off); ant_value_t result = js_eval_bytecode_eval_with_strict( js, str, len, sv_frame_is_strict(frame)); if (!is_err(result)) vm->stack[vm->sp++] = result; diff --git a/src/silver/ops/coercion.h b/src/silver/ops/coercion.h index 03811b8..7d205a8 100644 --- a/src/silver/ops/coercion.h +++ b/src/silver/ops/coercion.h @@ -23,7 +23,7 @@ static inline ant_value_t sv_module_export_cstr( if (is_err(set_res)) return set_res; if (len == 7 && memcmp(name, "default", 7) == 0) - js_set_slot(js, module_ns, SLOT_DEFAULT, value); + js_set_slot_wb(js, module_ns, SLOT_DEFAULT, value); return tov(0); } @@ -37,7 +37,7 @@ static inline ant_value_t sv_op_to_object(sv_vm_t *vm, ant_t *js) { "Cannot convert undefined or null to object"); ant_value_t obj = mkobj(js, 0); ant_value_t obj_as_obj = js_as_obj(obj); - js_set_slot(js, obj_as_obj, SLOT_PRIMITIVE, v); + js_set_slot(obj_as_obj, SLOT_PRIMITIVE, v); vm->stack[vm->sp - 1] = obj; return tov(0); } @@ -80,7 +80,7 @@ static inline ant_value_t sv_op_import_sync(sv_vm_t *vm, ant_t *js) { static inline void sv_op_import_default(sv_vm_t *vm, ant_t *js) { ant_value_t ns = vm->stack[vm->sp - 1]; if (vtype(ns) == T_OBJ) { - ant_value_t slot_val = js_get_slot(js, ns, SLOT_DEFAULT); + ant_value_t slot_val = js_get_slot(ns, SLOT_DEFAULT); if (vtype(slot_val) != T_UNDEF) { vm->stack[vm->sp - 1] = slot_val; return; } } } @@ -243,8 +243,8 @@ static inline void sv_op_with_del_var( } } - char tmp[256]; - bool ok = js_del(js, js->global, sv_atom_cstr(a, tmp, sizeof(tmp))); + ant_value_t result = js_delete_prop(js, js->global, a->str, a->len); + bool ok = !is_err(result) && js_truthy(js, result); vm->stack[vm->sp++] = mkval(T_BOOL, ok); } @@ -267,29 +267,27 @@ static inline void sv_op_special_obj( } ant_value_t arr = js_mkarr(js); - ant_handle_t h = js_root(js, arr); if (frame->bp && frame->argc > 0) { for (int i = 0; i < frame->argc; i++) - js_arr_push(js, js_deref(js, h), frame->bp[i]); + js_arr_push(js, arr, frame->bp[i]); } if (sv_frame_is_strict(frame)) - js_set_slot(js, js_deref(js, h), SLOT_STRICT_ARGS, tov(1)); + js_set_slot(arr, SLOT_STRICT_ARGS, tov(1)); else if (vtype(frame->callee) == T_FUNC) - setprop_cstr(js, js_deref(js, h), "callee", 6, frame->callee); + setprop_cstr(js, arr, "callee", 6, frame->callee); - js_set_sym(js, js_deref(js, h), get_toStringTag_sym(), js_mkstr(js, "Arguments", 9)); + js_set_sym(js, arr, get_toStringTag_sym(), js_mkstr(js, "Arguments", 9)); ant_value_t array_proto = js_get_ctor_proto(js, "Array", 5); if (is_object_type(array_proto)) { ant_value_t iter_fn = js_get_sym(js, array_proto, get_iterator_sym()); if (vtype(iter_fn) == T_FUNC || vtype(iter_fn) == T_CFUNC) - js_set_sym(js, js_deref(js, h), get_iterator_sym(), iter_fn); + js_set_sym(js, arr, get_iterator_sym(), iter_fn); } - vm->stack[vm->sp++] = js_deref(js, h); - js_unroot(js, h); + vm->stack[vm->sp++] = arr; } #endif diff --git a/src/silver/ops/comparison.h b/src/silver/ops/comparison.h index ac7b6ed..fe68b07 100644 --- a/src/silver/ops/comparison.h +++ b/src/silver/ops/comparison.h @@ -2,6 +2,7 @@ #define SV_COMPARISON_H #include "silver/engine.h" +#include "shapes.h" #include "modules/bigint.h" static inline void sv_op_seq(sv_vm_t *vm, ant_t *js) { @@ -17,19 +18,19 @@ static inline void sv_op_sne(sv_vm_t *vm, ant_t *js) { } static inline ant_value_t sv_abstract_eq(ant_t *js, ant_value_t l, ant_value_t r) { - uint8_t lt = vtype(l), rt = vtype(r); + uint8_t lt = vtype(l), rty = vtype(r); - if ((lt == T_NULL && rt == T_NULL) || (lt == T_UNDEF && rt == T_UNDEF) || - (lt == T_UNDEF && rt == T_NULL) || (lt == T_NULL && rt == T_UNDEF)) + if ((lt == T_NULL && rty == T_NULL) || (lt == T_UNDEF && rty == T_UNDEF) || + (lt == T_UNDEF && rty == T_NULL) || (lt == T_NULL && rty == T_UNDEF)) return mkval(T_BOOL, 1); - if (lt == T_NULL || rt == T_NULL || lt == T_UNDEF || rt == T_UNDEF) + if (lt == T_NULL || rty == T_NULL || lt == T_UNDEF || rty == T_UNDEF) return mkval(T_BOOL, 0); - if (lt == rt) + if (lt == rty) return mkval(T_BOOL, strict_eq_values(js, l, r)); - if ((lt == T_BIGINT && rt == T_NUM) || (lt == T_NUM && rt == T_BIGINT)) { + if ((lt == T_BIGINT && rty == T_NUM) || (lt == T_NUM && rty == T_BIGINT)) { double num_val = lt == T_NUM ? tod(l) : tod(r); ant_value_t bigint_val = lt == T_BIGINT ? l : r; if (isfinite(num_val) && num_val == trunc(num_val)) { @@ -45,9 +46,9 @@ static inline ant_value_t sv_abstract_eq(ant_t *js, ant_value_t l, ant_value_t r } if (lt == T_BOOL) return sv_abstract_eq(js, tov(vdata(l) ? 1.0 : 0.0), r); - if (rt == T_BOOL) return sv_abstract_eq(js, l, tov(vdata(r) ? 1.0 : 0.0)); + if (rty == T_BOOL) return sv_abstract_eq(js, l, tov(vdata(r) ? 1.0 : 0.0)); - if ((lt == T_NUM && rt == T_STR) || (lt == T_STR && rt == T_NUM)) + if ((lt == T_NUM && rty == T_STR) || (lt == T_STR && rty == T_NUM)) return mkval(T_BOOL, js_to_number(js, l) == js_to_number(js, r)); if (is_object_type(l)) { @@ -78,7 +79,7 @@ static inline int sv_strcmp(ant_t *js, ant_value_t l, ant_value_t r) { ant_offset_t n1, off1 = vstr(js, l, &n1); ant_offset_t n2, off2 = vstr(js, r, &n2); ant_offset_t min_len = n1 < n2 ? n1 : n2; - int cmp = memcmp(&js->mem[off1], &js->mem[off2], min_len); + int cmp = memcmp((const void *)(uintptr_t)off1, (const void *)(uintptr_t)off2, min_len); if (cmp == 0) return (n1 < n2) ? -1 : (n1 > n2) ? 1 : 0; return cmp < 0 ? -1 : 1; } @@ -98,18 +99,18 @@ static inline ant_value_t sv_op_lt(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; sv_coerce_relational(js, &l, &r); - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_NUM && rt == T_NUM) { + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_NUM && rty == T_NUM) { vm->stack[vm->sp++] = mkval(T_BOOL, tod(l) < tod(r)); return tov(0); } - if (lt == T_BIGINT && rt == T_BIGINT) { + if (lt == T_BIGINT && rty == T_BIGINT) { vm->stack[vm->sp++] = mkval(T_BOOL, bigint_compare(js, l, r) < 0); return tov(0); } - if (lt == T_BIGINT || rt == T_BIGINT) + if (lt == T_BIGINT || rty == T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - if (lt == T_STR && rt == T_STR) { + if (lt == T_STR && rty == T_STR) { vm->stack[vm->sp++] = mkval(T_BOOL, sv_strcmp(js, l, r) < 0); return tov(0); } @@ -121,18 +122,18 @@ static inline ant_value_t sv_op_le(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; sv_coerce_relational(js, &l, &r); - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_NUM && rt == T_NUM) { + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_NUM && rty == T_NUM) { vm->stack[vm->sp++] = mkval(T_BOOL, tod(l) <= tod(r)); return tov(0); } - if (lt == T_BIGINT && rt == T_BIGINT) { + if (lt == T_BIGINT && rty == T_BIGINT) { vm->stack[vm->sp++] = mkval(T_BOOL, bigint_compare(js, l, r) <= 0); return tov(0); } - if (lt == T_BIGINT || rt == T_BIGINT) + if (lt == T_BIGINT || rty == T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - if (lt == T_STR && rt == T_STR) { + if (lt == T_STR && rty == T_STR) { vm->stack[vm->sp++] = mkval(T_BOOL, sv_strcmp(js, l, r) <= 0); return tov(0); } @@ -144,18 +145,18 @@ static inline ant_value_t sv_op_gt(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; sv_coerce_relational(js, &l, &r); - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_NUM && rt == T_NUM) { + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_NUM && rty == T_NUM) { vm->stack[vm->sp++] = mkval(T_BOOL, tod(l) > tod(r)); return tov(0); } - if (lt == T_BIGINT && rt == T_BIGINT) { + if (lt == T_BIGINT && rty == T_BIGINT) { vm->stack[vm->sp++] = mkval(T_BOOL, bigint_compare(js, l, r) > 0); return tov(0); } - if (lt == T_BIGINT || rt == T_BIGINT) + if (lt == T_BIGINT || rty == T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - if (lt == T_STR && rt == T_STR) { + if (lt == T_STR && rty == T_STR) { vm->stack[vm->sp++] = mkval(T_BOOL, sv_strcmp(js, l, r) > 0); return tov(0); } @@ -167,18 +168,18 @@ static inline ant_value_t sv_op_ge(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; sv_coerce_relational(js, &l, &r); - uint8_t lt = vtype(l), rt = vtype(r); - if (lt == T_NUM && rt == T_NUM) { + uint8_t lt = vtype(l), rty = vtype(r); + if (lt == T_NUM && rty == T_NUM) { vm->stack[vm->sp++] = mkval(T_BOOL, tod(l) >= tod(r)); return tov(0); } - if (lt == T_BIGINT && rt == T_BIGINT) { + if (lt == T_BIGINT && rty == T_BIGINT) { vm->stack[vm->sp++] = mkval(T_BOOL, bigint_compare(js, l, r) >= 0); return tov(0); } - if (lt == T_BIGINT || rt == T_BIGINT) + if (lt == T_BIGINT || rty == T_BIGINT) return js_mkerr(js, "Cannot mix BigInt value and other types"); - if (lt == T_STR && rt == T_STR) { + if (lt == T_STR && rty == T_STR) { vm->stack[vm->sp++] = mkval(T_BOOL, sv_strcmp(js, l, r) >= 0); return tov(0); } @@ -186,14 +187,88 @@ static inline ant_value_t sv_op_ge(sv_vm_t *vm, ant_t *js) { return tov(0); } -static inline ant_value_t sv_op_instanceof(sv_vm_t *vm, ant_t *js) { +static inline sv_ic_entry_t *sv_instanceof_ic_slot(sv_func_t *func, uint8_t *ip) { + if (!func || !func->ic_slots || !ip) return NULL; + uint16_t ic_idx = sv_get_u16(ip + 1); + if (ic_idx == UINT16_MAX || ic_idx >= func->ic_count) return NULL; + return &func->ic_slots[ic_idx]; +} + +static inline sv_ic_entry_t *sv_isproto_ic_slot(sv_func_t *func, uint8_t *ip) { + if (!func || !func->ic_slots || !ip) return NULL; + uint16_t ic_idx = sv_get_u16(ip + 1); + if (ic_idx == UINT16_MAX || ic_idx >= func->ic_count) return NULL; + return &func->ic_slots[ic_idx]; +} + +static inline ant_value_t sv_instanceof_ic_eval( + ant_t *js, ant_value_t l, ant_value_t r, + sv_func_t *func, uint8_t *ip +) { + sv_ic_entry_t *ic = sv_instanceof_ic_slot(func, ip); + if (!ic || !is_object_type(l) || vtype(r) != T_FUNC) goto slow_path; + + uint32_t cur_epoch = ant_ic_epoch_counter; + uintptr_t rhs_id = (uintptr_t)vdata(r); + if (ic->epoch != cur_epoch || ic->cached_aux != rhs_id) goto slow_path; + + ant_object_t *lhs_ptr = js_obj_ptr(js_as_obj(l)); + if (lhs_ptr && lhs_ptr->shape == ic->cached_shape) + return js_bool(ic->cached_index != 0); + +slow_path: + ant_value_t res = do_instanceof(js, l, r); + lhs_ptr = is_object_type(l) ? js_obj_ptr(js_as_obj(l)) : NULL; + + if (!is_err(res) && ic && lhs_ptr && vtype(r) == T_FUNC && vtype(res) == T_BOOL) { + ic->cached_shape = lhs_ptr->shape; + ic->cached_holder = NULL; + ic->cached_index = (uint32_t)(vdata(res) ? 1u : 0u); + ic->epoch = ant_ic_epoch_counter; + ic->cached_aux = (uintptr_t)vdata(r); + } + + return res; +} + +static inline ant_value_t sv_op_instanceof( + sv_vm_t *vm, ant_t *js, + sv_func_t *func, uint8_t *ip +) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; - ant_value_t res = do_instanceof(js, l, r); + ant_value_t res = sv_instanceof_ic_eval(js, l, r, func, ip); if (!is_err(res)) vm->stack[vm->sp++] = res; return res; } +static inline ant_value_t sv_isproto_ic_eval( + ant_t *js, ant_value_t proto_obj, ant_value_t obj, + sv_func_t *func, uint8_t *ip +) { + sv_ic_entry_t *ic = sv_isproto_ic_slot(func, ip); + ant_object_t *proto_ptr = is_object_type(proto_obj) ? js_obj_ptr(js_as_obj(proto_obj)) : NULL; + ant_object_t *obj_ptr = is_object_type(obj) ? js_obj_ptr(js_as_obj(obj)) : NULL; + + if ( + ic && proto_ptr && obj_ptr && + ic->epoch == ant_ic_epoch_counter && + ic->cached_holder == proto_ptr && + (ant_object_t *)(uintptr_t)ic->cached_shape == obj_ptr + ) { + return js_bool(ic->cached_index != 0); + } + + bool found = js_is_prototype_of(js, proto_obj, obj); + if (ic && proto_ptr && obj_ptr) { + ic->cached_holder = proto_ptr; + ic->cached_shape = (ant_shape_t *)(uintptr_t)obj_ptr; + ic->cached_index = found ? 1u : 0u; + ic->epoch = ant_ic_epoch_counter; + } + return js_bool(found); +} + static inline ant_value_t sv_op_in(sv_vm_t *vm, ant_t *js) { ant_value_t r = vm->stack[--vm->sp]; ant_value_t l = vm->stack[--vm->sp]; diff --git a/src/silver/ops/globals.h b/src/silver/ops/globals.h index d4ddf6c..fb7ca9c 100644 --- a/src/silver/ops/globals.h +++ b/src/silver/ops/globals.h @@ -3,11 +3,91 @@ #include "silver/engine.h" #include "errors.h" +#include "shapes.h" static inline ant_value_t sv_global_get(ant_t *js, const char *str, uint32_t len) { - ant_offset_t off = lkp(js, js->global, str, len); - if (off == 0) return js_mkundef(); - return resolveprop(js, mkval(T_PROP, off)); + if (!str) return js_mkundef(); + const char *interned = intern_string(str, len); + if (!interned) return js_mkundef(); + return lkp_interned_val(js, js->global, interned); +} + +static inline sv_ic_entry_t *sv_global_ic_slot_for_ip(sv_func_t *func, uint8_t *ip) { + if (!func || !func->ic_slots || !ip) return NULL; + uint16_t ic_idx = sv_get_u16(ip + 5); + if (ic_idx == UINT16_MAX || ic_idx >= func->ic_count) return NULL; + return &func->ic_slots[ic_idx]; +} + +static inline ant_object_t *sv_global_obj_ptr(ant_t *js) { + if (!is_object_type(js->global)) return NULL; + return js_obj_ptr(js_as_obj(js->global)); +} + +static inline bool sv_global_ic_try_get_hit( + ant_t *js, + sv_ic_entry_t *ic, + const char *interned, + ant_value_t *out +) { + if (!ic || !interned) return false; + + ant_object_t *gptr = sv_global_obj_ptr(js); + if (!gptr || gptr->is_exotic || !gptr->shape) return false; + if (ic->epoch != ant_ic_epoch_counter) return false; + if (ic->cached_shape != gptr->shape) return false; + if (ic->cached_index >= gptr->prop_count) return false; + + const ant_shape_prop_t *prop = ant_shape_prop_at(gptr->shape, ic->cached_index); + if (!prop) return false; + if (prop->type != ANT_SHAPE_KEY_STRING || prop->key.interned != interned) return false; + if (prop->has_getter || prop->has_setter) return false; + + *out = ant_object_prop_get_unchecked(gptr, ic->cached_index); + return true; +} + +static inline bool sv_global_ic_try_fill( + ant_t *js, + sv_ic_entry_t *ic, + const char *interned, + ant_value_t *out +) { + if (!ic || !interned) return false; + + ant_object_t *gptr = sv_global_obj_ptr(js); + if (!gptr || gptr->is_exotic || !gptr->shape) return false; + + int32_t slot = ant_shape_lookup_interned(gptr->shape, interned); + if (slot < 0) return false; + + uint32_t idx = (uint32_t)slot; + if (idx >= gptr->prop_count) return false; + + const ant_shape_prop_t *prop = ant_shape_prop_at(gptr->shape, idx); + if (!prop) return false; + if (prop->type != ANT_SHAPE_KEY_STRING || prop->key.interned != interned) return false; + if (prop->has_getter || prop->has_setter) return false; + + ic->cached_shape = gptr->shape; + ic->cached_holder = gptr; + ic->cached_index = idx; + ic->epoch = ant_ic_epoch_counter; + *out = ant_object_prop_get_unchecked(gptr, idx); + return true; +} + +static inline ant_value_t sv_global_get_interned_ic( + ant_t *js, + const char *interned, + sv_func_t *func, + uint8_t *ip +) { + ant_value_t out = js_mkundef(); + sv_ic_entry_t *ic = sv_global_ic_slot_for_ip(func, ip); + if (sv_global_ic_try_get_hit(js, ic, interned, &out)) return out; + if (sv_global_ic_try_fill(js, ic, interned, &out)) return out; + return lkp_interned_val(js, js->global, interned); } static inline ant_value_t sv_op_get_global( @@ -22,7 +102,7 @@ static inline ant_value_t sv_op_get_global( vm->stack[vm->sp++] = sv; return sv; } - ant_value_t val = sv_global_get(js, a->str, a->len); + ant_value_t val = sv_global_get_interned_ic(js, a->str, func, ip); if (is_undefined(val)) return js_mkerr_typed( js, JS_ERR_REFERENCE, "'%.*s' is not defined", @@ -36,7 +116,7 @@ static inline void sv_op_get_global_undef( sv_func_t *func, uint8_t *ip ) { sv_atom_t *a = &func->atoms[sv_get_u32(ip + 1)]; - vm->stack[vm->sp++] = sv_global_get(js, a->str, a->len); + vm->stack[vm->sp++] = sv_global_get_interned_ic(js, a->str, func, ip); } static inline ant_value_t sv_op_put_global( @@ -44,7 +124,7 @@ static inline ant_value_t sv_op_put_global( sv_frame_t *frame, sv_func_t *func, uint8_t *ip ) { sv_atom_t *a = &func->atoms[sv_get_u32(ip + 1)]; - if (sv_frame_is_strict(frame) && lkp(js, js->global, a->str, a->len) == 0) + if (sv_frame_is_strict(frame) && lkp_interned(js, js->global, a->str, a->len) == 0) return js_mkerr_typed( js, JS_ERR_REFERENCE, "'%.*s' is not defined", (int)a->len, a->str); diff --git a/src/silver/ops/iteration.h b/src/silver/ops/iteration.h index a4fc6a1..2af4e99 100644 --- a/src/silver/ops/iteration.h +++ b/src/silver/ops/iteration.h @@ -28,7 +28,7 @@ static inline bool sv_is_map_iter( iter_type_t *out_type ) { if (vtype(obj) != T_OBJ) return false; - ant_value_t state_val = js_get_slot(js, obj, SLOT_ITER_STATE); + ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return false; map_iterator_state_t *st = (map_iterator_state_t *)(uintptr_t)js_getnum(state_val); @@ -47,7 +47,7 @@ static inline bool sv_is_set_iter( iter_type_t *out_type ) { if (vtype(obj) != T_OBJ) return false; - ant_value_t state_val = js_get_slot(js, obj, SLOT_ITER_STATE); + ant_value_t state_val = js_get_slot(obj, SLOT_ITER_STATE); if (vtype(state_val) == T_UNDEF) return false; set_iterator_state_t *st = (set_iterator_state_t *)(uintptr_t)js_getnum(state_val); @@ -73,7 +73,7 @@ static inline ant_value_t sv_op_for_of(sv_vm_t *vm, ant_t *js) { } if (vtype(iterable) == T_STR) { - if (is_rope(js, iterable)) { + if (str_is_heap_rope(iterable)) { iterable = rope_flatten(js, iterable); if (is_err(iterable)) return iterable; } @@ -220,8 +220,12 @@ static inline ant_value_t sv_op_iter_next(sv_vm_t *vm, ant_t *js, uint8_t *ip) { } else { ant_offset_t off = vstr(js, str, NULL); utf8proc_int32_t cp; - ant_offset_t cb_len = (ant_offset_t)utf8_next((const utf8proc_uint8_t *)&js->mem[off + idx], (utf8proc_ssize_t)(slen - idx), &cp); - vm->stack[vm->sp++] = js_mkstr(js, (char *)&js->mem[off + idx], cb_len); + ant_offset_t cb_len = (ant_offset_t)utf8_next( + (const utf8proc_uint8_t *)(uintptr_t)(off + idx), + (utf8proc_ssize_t)(slen - idx), + &cp + ); + vm->stack[vm->sp++] = js_mkstr(js, (const void *)(uintptr_t)(off + idx), cb_len); vm->stack[vm->sp++] = js_false; vm->stack[vm->sp - 4] = tov(idx + (int)cb_len); } diff --git a/src/silver/ops/literals.h b/src/silver/ops/literals.h index adfc06d..ddbd860 100644 --- a/src/silver/ops/literals.h +++ b/src/silver/ops/literals.h @@ -43,24 +43,47 @@ static inline void sv_op_global(sv_vm_t *vm, ant_t *js) { vm->stack[vm->sp++] = js->global; } -static inline void sv_op_object(sv_vm_t *vm, ant_t *js) { +static inline sv_obj_site_cache_t *sv_obj_site_for_ip(sv_func_t *func, uint8_t *ip) { + if (!func || !ip || !func->obj_sites || func->obj_site_count == 0) return NULL; + uint32_t off = (uint32_t)(ip - func->code); + for (uint16_t i = 0; i < func->obj_site_count; i++) { + if (func->obj_sites[i].bc_off == off) return &func->obj_sites[i]; + } + return NULL; +} + +static inline void sv_op_object(sv_vm_t *vm, ant_t *js, sv_func_t *func, uint8_t *ip) { ant_value_t obj = mkobj(js, 0); + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + sv_obj_site_cache_t *site = sv_obj_site_for_ip(func, ip); + if (ptr && ptr->shape && site) { + if (site->shared_shape) { + if (site->shared_shape != ptr->shape) { + ant_shape_retain(site->shared_shape); + ant_shape_release(ptr->shape); + ptr->shape = site->shared_shape; + } + uint32_t count = ant_shape_count(ptr->shape); + if (count > ptr->prop_count) (void)js_obj_ensure_prop_capacity(ptr, count); + } else { + site->shared_shape = ptr->shape; + ant_shape_retain(site->shared_shape); + } + } ant_value_t proto = js_get_ctor_proto(js, "Object", 6); - if (vtype(proto) == T_OBJ) js_set_proto(js, obj, proto); + if (vtype(proto) == T_OBJ) js_set_proto_init(obj, proto); vm->stack[vm->sp++] = obj; } static inline void sv_op_array(sv_vm_t *vm, ant_t *js, uint8_t *ip) { uint16_t n = sv_get_u16(ip + 1); ant_value_t arr = js_mkarr(js); - ant_handle_t h = js_root(js, arr); for (uint16_t i = 0; i < n; i++) { ant_value_t val = vm->stack[vm->sp - n + i]; - js_arr_push(js, js_deref(js, h), val); + js_arr_push(js, arr, val); } vm->sp -= n; - vm->stack[vm->sp++] = js_deref(js, h); - js_unroot(js, h); + vm->stack[vm->sp++] = arr; } static inline void sv_op_regexp(sv_vm_t *vm, ant_t *js) { @@ -69,19 +92,18 @@ static inline void sv_op_regexp(sv_vm_t *vm, ant_t *js) { vm->sp -= 2; ant_value_t regexp_obj = mkobj(js, 0); - ant_handle_t h = js_root(js, regexp_obj); ant_value_t regexp_proto = js_get_ctor_proto(js, "RegExp", 6); - if (vtype(regexp_proto) == T_OBJ) js_set_proto(js, js_deref(js, h), regexp_proto); + if (vtype(regexp_proto) == T_OBJ) js_set_proto_init(regexp_obj, regexp_proto); - js_mkprop_fast(js, js_deref(js, h), "source", 6, pattern); + js_mkprop_fast(js, regexp_obj, "source", 6, pattern); ant_offset_t flen = 0; const char *fstr = ""; if (vtype(flags) == T_STR) { ant_offset_t foff; foff = vstr(js, flags, &flen); - fstr = (const char *)&js->mem[foff]; + fstr = (const char *)(uintptr_t)(foff); } bool g = false, i = false, m = false, s = false, y = false; @@ -100,17 +122,15 @@ static inline void sv_op_regexp(sv_vm_t *vm, ant_t *js) { if (s) sorted[si++] = 's'; if (y) sorted[si++] = 'y'; - ant_value_t obj = js_deref(js, h); - js_mkprop_fast(js, obj, "flags", 5, js_mkstr(js, sorted, si)); - js_mkprop_fast(js, obj, "global", 6, mkval(T_BOOL, g ? 1 : 0)); - js_mkprop_fast(js, obj, "ignoreCase", 10, mkval(T_BOOL, i ? 1 : 0)); - js_mkprop_fast(js, obj, "multiline", 9, mkval(T_BOOL, m ? 1 : 0)); - js_mkprop_fast(js, obj, "dotAll", 6, mkval(T_BOOL, s ? 1 : 0)); - js_mkprop_fast(js, obj, "sticky", 6, mkval(T_BOOL, y ? 1 : 0)); - js_mkprop_fast(js, obj, "lastIndex", 9, tov(0)); - - vm->stack[vm->sp++] = js_deref(js, h); - js_unroot(js, h); + js_mkprop_fast(js, regexp_obj, "flags", 5, js_mkstr(js, sorted, si)); + js_mkprop_fast(js, regexp_obj, "global", 6, mkval(T_BOOL, g ? 1 : 0)); + js_mkprop_fast(js, regexp_obj, "ignoreCase", 10, mkval(T_BOOL, i ? 1 : 0)); + js_mkprop_fast(js, regexp_obj, "multiline", 9, mkval(T_BOOL, m ? 1 : 0)); + js_mkprop_fast(js, regexp_obj, "dotAll", 6, mkval(T_BOOL, s ? 1 : 0)); + js_mkprop_fast(js, regexp_obj, "sticky", 6, mkval(T_BOOL, y ? 1 : 0)); + js_mkprop_fast(js, regexp_obj, "lastIndex", 9, tov(0)); + + vm->stack[vm->sp++] = regexp_obj; } #endif diff --git a/src/silver/ops/locals.h b/src/silver/ops/locals.h index ad8d81b..b6c1709 100644 --- a/src/silver/ops/locals.h +++ b/src/silver/ops/locals.h @@ -9,14 +9,16 @@ static inline void sv_op_get_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { vm->stack[vm->sp++] = lp[idx]; } -static inline void sv_op_put_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_put_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint16_t idx = sv_get_u16(ip + 1); lp[idx] = vm->stack[--vm->sp]; + sv_tfb_record_local(func, (int)idx, lp[idx]); } -static inline void sv_op_set_local(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_set_local(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint16_t idx = sv_get_u16(ip + 1); lp[idx] = vm->stack[vm->sp - 1]; + sv_tfb_record_local(func, (int)idx, lp[idx]); } static inline void sv_op_get_local8(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { @@ -24,14 +26,16 @@ static inline void sv_op_get_local8(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { vm->stack[vm->sp++] = lp[idx]; } -static inline void sv_op_put_local8(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_put_local8(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint8_t idx = sv_get_u8(ip + 1); lp[idx] = vm->stack[--vm->sp]; + sv_tfb_record_local(func, (int)idx, lp[idx]); } -static inline void sv_op_set_local8(sv_vm_t *vm, ant_value_t *lp, uint8_t *ip) { +static inline void sv_op_set_local8(sv_vm_t *vm, ant_value_t *lp, sv_func_t *func, uint8_t *ip) { uint8_t idx = sv_get_u8(ip + 1); lp[idx] = vm->stack[vm->sp - 1]; + sv_tfb_record_local(func, (int)idx, lp[idx]); } static inline void sv_op_set_local_undef(ant_value_t *lp, uint8_t *ip) { @@ -104,13 +108,11 @@ static inline void sv_op_rest( ) { uint16_t start = sv_get_u16(ip + 1); ant_value_t arr = js_mkarr(js); - ant_handle_t h = js_root(js, arr); if (frame->bp) { for (int i = (int)start; i < frame->argc; i++) - js_arr_push(js, js_deref(js, h), frame->bp[i]); + js_arr_push(js, arr, frame->bp[i]); } - vm->stack[vm->sp++] = js_deref(js, h); - js_unroot(js, h); + vm->stack[vm->sp++] = arr; } #endif diff --git a/src/silver/ops/objects.h b/src/silver/ops/objects.h index 32cfd1d..5948293 100644 --- a/src/silver/ops/objects.h +++ b/src/silver/ops/objects.h @@ -17,14 +17,15 @@ static inline void sv_op_define_method( sv_atom_t *a = &func->atoms[atom_idx]; ant_value_t fn = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[vm->sp - 1]; + ant_value_t desc_obj = js_as_obj(obj); bool is_getter = (flags & 1) != 0; bool is_setter = (flags & 2) != 0; if (is_getter) { - js_set_getter_desc(js, obj, a->str, a->len, fn, JS_DESC_C); + js_set_getter_desc(js, desc_obj, a->str, a->len, fn, JS_DESC_C); return; } if (is_setter) { - js_set_setter_desc(js, obj, a->str, a->len, fn, JS_DESC_C); + js_set_setter_desc(js, desc_obj, a->str, a->len, fn, JS_DESC_C); return; } ant_value_t key = js_mkstr(js, a->str, a->len); @@ -39,6 +40,7 @@ static inline void sv_op_define_method_comp( ant_value_t fn = vm->stack[--vm->sp]; ant_value_t key = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[vm->sp - 1]; + ant_value_t desc_obj = js_as_obj(obj); bool is_getter = (flags & 1) != 0; bool is_setter = (flags & 2) != 0; if (vtype(key) == T_SYMBOL && !is_getter && !is_setter) { @@ -49,15 +51,15 @@ static inline void sv_op_define_method_comp( if ((is_getter || is_setter) && vtype(key_str) == T_STR) { ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; - if (is_getter) js_set_getter_desc(js, obj, kptr, klen, fn, JS_DESC_C); - else js_set_setter_desc(js, obj, kptr, klen, fn, JS_DESC_C); + const char *kptr = (const char *)(uintptr_t)(koff); + if (is_getter) js_set_getter_desc(js, desc_obj, kptr, klen, fn, JS_DESC_C); + else js_set_setter_desc(js, desc_obj, kptr, klen, fn, JS_DESC_C); return; } if (vtype(key_str) == T_STR) { ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); js_define_own_prop(js, obj, kptr, (size_t)klen, fn); } else mkprop(js, obj, key_str, fn, 0); } @@ -83,9 +85,9 @@ static inline void sv_op_set_name_comp(sv_vm_t *vm, ant_t *js) { static inline void sv_op_set_proto(sv_vm_t *vm, ant_t *js) { ant_value_t proto = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[vm->sp - 1]; + uint8_t pt = vtype(proto); - if (pt == T_OBJ || pt == T_NULL || pt == T_FUNC || pt == T_ARR) - js_set_proto(js, obj, proto); + if (pt == T_OBJ || pt == T_NULL || pt == T_FUNC || pt == T_ARR) js_set_proto_wb(js, obj, proto); } static inline void sv_op_set_home_obj(sv_vm_t *vm, ant_t *js) { @@ -141,7 +143,7 @@ static inline ant_value_t sv_op_spread(sv_vm_t *vm, ant_t *js) { } if (vtype(iterable) == T_STR) { - if (is_rope(js, iterable)) { + if (str_is_heap_rope(iterable)) { iterable = rope_flatten(js, iterable); if (is_err(iterable)) return iterable; } @@ -150,10 +152,10 @@ static inline ant_value_t sv_op_spread(sv_vm_t *vm, ant_t *js) { ant_offset_t off = vstr(js, iterable, NULL); utf8proc_int32_t cp; ant_offset_t cb_len = (ant_offset_t)utf8_next( - (const utf8proc_uint8_t *)&js->mem[off + i], + (const utf8proc_uint8_t *)(uintptr_t)(off + i), (utf8proc_ssize_t)(slen - i), &cp ); - js_arr_push(js, arr, js_mkstr(js, (char *)&js->mem[off + i], cb_len)); + js_arr_push(js, arr, js_mkstr(js, (const void *)(uintptr_t)(off + i), cb_len)); i += cb_len; } return tov(0); @@ -169,19 +171,17 @@ static inline ant_value_t sv_op_spread(sv_vm_t *vm, ant_t *js) { if (!is_object_type(iterator)) return js_mkerr(js, "not iterable"); - ant_handle_t hiter = js_root(js, iterator); ant_value_t status = tov(0); for (;;) { - ant_value_t cur_iter = js_deref(js, hiter); - ant_value_t next_method = js_getprop_fallback(js, cur_iter, "next"); + ant_value_t next_method = js_getprop_fallback(js, iterator, "next"); ft = vtype(next_method); if (ft != T_FUNC && ft != T_CFUNC) { status = js_mkerr(js, "iterator.next is not a function"); break; } - ant_value_t result = sv_vm_call(vm, js, next_method, cur_iter, NULL, 0, NULL, false); + ant_value_t result = sv_vm_call(vm, js, next_method, iterator, NULL, 0, NULL, false); if (is_err(result)) { status = result; break; @@ -199,7 +199,6 @@ static inline ant_value_t sv_op_spread(sv_vm_t *vm, ant_t *js) { js_arr_push(js, arr, value); } - js_unroot(js, hiter); return status; } @@ -222,35 +221,29 @@ static inline void sv_op_define_class( if (vtype(ctor) == T_UNDEF) { ant_value_t ctor_obj = mkobj(js, 0); ctor = js_obj_to_func_ex(ctor_obj, SV_CALL_IS_DEFAULT_CTOR); - ant_value_t func_proto = js_get_slot(js, js->global, SLOT_FUNC_PROTO); + ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); if (vtype(func_proto) == T_FUNC) - js_set_proto(js, ctor_obj, func_proto); + js_set_proto_init(ctor_obj, func_proto); } ant_value_t proto = mkobj(js, 0); - ant_handle_t hp = js_root(js, proto); - ant_handle_t hc = js_root(js, ctor); if (pt == T_NULL) { - js_set_proto(js, js_deref(js, hp), js_mknull()); - ant_value_t func_proto = js_get_slot(js, js->global, SLOT_FUNC_PROTO); - if (vtype(func_proto) == T_FUNC) - js_set_proto(js, js_deref(js, hc), func_proto); + js_set_proto_init(proto, js_mknull()); + ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); + if (vtype(func_proto) == T_FUNC) js_set_proto_wb(js, ctor, func_proto); } else if (parent_is_object) { ant_value_t parent_proto = js_getprop_fallback(js, parent, "prototype"); - if (is_object_type(parent_proto)) - js_set_proto(js, js_deref(js, hp), parent_proto); - js_set_proto(js, js_deref(js, hc), parent); + if (is_object_type(parent_proto)) js_set_proto_init(proto, parent_proto); + js_set_proto_wb(js, ctor, parent); } else { ant_value_t object_ctor = js_getprop_fallback(js, js->global, "Object"); if (vtype(object_ctor) == T_FUNC) { ant_value_t object_proto = js_getprop_fallback(js, object_ctor, "prototype"); - if (is_object_type(object_proto)) - js_set_proto(js, js_deref(js, hp), object_proto); + if (is_object_type(object_proto)) js_set_proto_init(proto, object_proto); } } if (parent_is_callable) { - ctor = js_deref(js, hc); if (vtype(ctor) == T_FUNC) { sv_closure_t *c = js_func_closure(ctor); c->super_val = parent; @@ -258,16 +251,12 @@ static inline void sv_op_define_class( } } - ctor = js_deref(js, hc); - proto = js_deref(js, hp); + if (vtype(ctor) == T_FUNC) js_mark_constructor(js_func_obj(ctor), true); setprop_interned(js, proto, "constructor", 11, ctor); setprop_interned(js, ctor, "prototype", 9, proto); if (a && a->len > 0) setprop_cstr(js, ctor, "name", 4, js_mkstr(js, a->str, a->len)); - js_unroot(js, hc); - js_unroot(js, hp); - vm->stack[vm->sp - 2] = ctor; vm->stack[vm->sp - 1] = proto; } diff --git a/src/silver/ops/private.h b/src/silver/ops/private.h index f795405..b8eab3b 100644 --- a/src/silver/ops/private.h +++ b/src/silver/ops/private.h @@ -9,7 +9,7 @@ static inline void sv_op_get_private(sv_vm_t *vm, ant_t *js) { ant_value_t key_str = coerce_to_str(js, prop); ant_offset_t klen; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); vm->stack[vm->sp++] = js_getprop_fallback(js, obj, kptr); } diff --git a/src/silver/ops/property.h b/src/silver/ops/property.h index c87404f..7954af0 100644 --- a/src/silver/ops/property.h +++ b/src/silver/ops/property.h @@ -2,6 +2,8 @@ #define SV_PROPERTY_H #include "silver/engine.h" +#include "shapes.h" +#include "gc.h" #include "utf8.h" #include #include @@ -30,6 +32,193 @@ static inline ant_value_t sv_key_to_propstr(ant_t *js, ant_value_t key) { return coerce_to_str(js, key); } +static inline ant_object_t *sv_array_obj_ptr(ant_value_t obj) { + if (!is_object_type(obj)) return NULL; + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + return (ptr && ptr->type_tag == T_ARR) ? ptr : NULL; +} + +static inline bool sv_try_get_shape_data_prop( + ant_t *js, + ant_object_t *ptr, + const char *interned, + ant_value_t *out, + bool *should_fallback +) { + if (!ptr) { + *should_fallback = true; + return false; + } + if (ptr->is_exotic) { + *should_fallback = true; + return false; + } + if (!ptr->shape) return false; + + int32_t slot = ant_shape_lookup_interned(ptr->shape, interned); + if (slot < 0) return false; + + uint32_t idx = (uint32_t)slot; + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, idx); + if (!prop) { + *out = js_mkundef(); + return true; + } + if (prop->has_getter || prop->has_setter) { + *should_fallback = true; + return false; + } + + *out = (idx < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, idx) : js_mkundef(); + return true; +} + +static inline bool sv_same_obj_identity(ant_value_t a, ant_value_t b) { + if (!is_object_type(a) || !is_object_type(b)) return false; + return vdata(js_as_obj(a)) == vdata(js_as_obj(b)); +} + +static inline ant_value_t sv_obj_proto_or_null(ant_value_t v) { + if (!is_object_type(v)) return js_mknull(); + ant_object_t *ptr = js_obj_ptr(js_as_obj(v)); + if (!ptr) return js_mknull(); + return is_object_type(ptr->proto) ? ptr->proto : js_mknull(); +} + +typedef struct { + int depth; + bool overflow; + ant_value_t fast; +} sv_proto_guard_t; + +static inline void sv_proto_guard_init(sv_proto_guard_t *g) { + g->depth = 0; + g->overflow = false; + g->fast = js_mknull(); +} + +static inline bool sv_proto_guard_hit_cycle(sv_proto_guard_t *g, ant_value_t cur) { + if (!g->overflow) { + if (++g->depth < MAX_PROTO_CHAIN_DEPTH) return false; + g->overflow = true; + g->fast = cur; + } + + g->fast = sv_obj_proto_or_null(g->fast); + g->fast = sv_obj_proto_or_null(g->fast); + return sv_same_obj_identity(cur, g->fast); +} + +static inline sv_ic_entry_t *sv_ic_slot_for_ip(sv_func_t *func, uint8_t *ip) { + if (!func || !func->ic_slots || !ip) return NULL; + uint16_t ic_idx = sv_get_u16(ip + 5); + if (ic_idx == UINT16_MAX || ic_idx >= func->ic_count) return NULL; + return &func->ic_slots[ic_idx]; +} + +static inline bool sv_ic_try_get_hit( + sv_ic_entry_t *ic, + ant_object_t *receiver, + sv_atom_t *a, + ant_value_t *out +) { + if (!ic || !receiver) return false; + if (ic->epoch != ant_ic_epoch_counter) return false; + if (ic->cached_shape != receiver->shape) return false; + + ant_object_t *source; + ant_shape_t *prop_shape; + + if (ic->cached_is_own) { + source = receiver; + prop_shape = receiver->shape; + } else { + ant_object_t *holder = ic->cached_holder; + if (!holder || holder->is_exotic || !holder->shape) return false; + source = holder; + prop_shape = holder->shape; + } + if (ic->cached_index >= source->prop_count) return false; + + const ant_shape_prop_t *prop = ant_shape_prop_at(prop_shape, ic->cached_index); + if (!prop) return false; + if (prop->type != ANT_SHAPE_KEY_STRING || prop->key.interned != a->str) return false; + if (prop->has_getter || prop->has_setter) return false; + + *out = ant_object_prop_get_unchecked(source, ic->cached_index); + return true; +} + +static inline bool sv_ic_probe_get_chain( + ant_value_t obj, + const char *interned, + ant_object_t **out_holder, + uint32_t *out_index, + ant_value_t *out_value +) { + ant_value_t cur = obj; + sv_proto_guard_t guard; + sv_proto_guard_init(&guard); + while (is_object_type(cur)) { + ant_object_t *ptr = js_obj_ptr(js_as_obj(cur)); + if (!ptr || ptr->is_exotic) return false; + if (!ptr->shape) { + ant_value_t next = ptr->proto; + if (!is_object_type(next)) break; + cur = next; + if (sv_proto_guard_hit_cycle(&guard, cur)) break; + continue; + } + + int32_t slot = ant_shape_lookup_interned(ptr->shape, interned); + if (slot < 0) { + ant_value_t next = ptr->proto; + if (!is_object_type(next)) break; + cur = next; + if (sv_proto_guard_hit_cycle(&guard, cur)) break; + continue; + } + + uint32_t idx = (uint32_t)slot; + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, idx); + if (!prop) return false; + if (prop->has_getter || prop->has_setter) return false; + + *out_holder = ptr; + *out_index = idx; + *out_value = (idx < ptr->prop_count) ? ant_object_prop_get_unchecked(ptr, idx) : js_mkundef(); + return true; + } + + return false; +} + +static inline void sv_gf_ic_note_success(sv_ic_entry_t *ic) { + if (!ic) return; + uintptr_t aux = ic->cached_aux; + uint8_t warmup = sv_gf_ic_warmup(aux); + if (warmup < 0xFFu) warmup++; + bool active = sv_gf_ic_active(aux) || warmup >= SV_GF_IC_WARMUP_ENABLE; + ic->cached_aux = sv_gf_ic_pack_aux(warmup, 0u, active); +} + +static inline void sv_gf_ic_note_miss(sv_ic_entry_t *ic) { + if (!ic) return; + uintptr_t aux = ic->cached_aux; + uint8_t warmup = sv_gf_ic_warmup(aux); + uint8_t miss = sv_gf_ic_miss_streak(aux); + bool active = sv_gf_ic_active(aux); + + if (miss < 0xFFu) miss++; + if (miss >= SV_GF_IC_MISS_DISABLE) { + warmup = 0; + miss = 0; + active = false; + } + + ic->cached_aux = sv_gf_ic_pack_aux(warmup, miss, active); +} + static inline ant_value_t sv_getprop_by_key(ant_t *js, ant_value_t obj, ant_value_t key) { if (vtype(key) == T_SYMBOL) return js_get_sym(js, obj, key); ant_value_t key_str = sv_key_to_propstr(js, key); @@ -37,7 +226,7 @@ static inline ant_value_t sv_getprop_by_key(ant_t *js, ant_value_t obj, ant_valu ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); return sv_getprop_fallback_len(js, obj, kptr, klen); } @@ -62,25 +251,46 @@ static inline ant_value_t sv_prop_get_at( } else if (t == T_SYMBOL) { sym_prim = obj; } else if (t == T_OBJ) { - ant_value_t prim = js_get_slot(js, obj, SLOT_PRIMITIVE); + ant_value_t prim = js_get_slot(obj, SLOT_PRIMITIVE); if (vtype(prim) == T_STR) str_prim = prim; else if (vtype(prim) == T_SYMBOL) sym_prim = prim; } - if (vtype(str_prim) == T_STR && len == 6 && memcmp(str, "length", 6) == 0) { + if (vtype(str_prim) == T_STR && is_length_key(str, len)) { ant_offset_t byte_len = 0; ant_offset_t str_off = vstr(js, str_prim, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); return tov((double)utf16_strlen(str_data, byte_len)); } + if (is_length_key(str, len)) { + ant_object_t *arr_ptr = sv_array_obj_ptr(obj); + if (arr_ptr) return tov((double)js_arr_len(js, js_as_obj(obj))); + } + if (vtype(sym_prim) == T_SYMBOL && len == 11 && memcmp(str, "description", 11) == 0) { const char *desc = js_sym_desc(js, sym_prim); if (desc) return js_mkstr(js, desc, strlen(desc)); return js_mkundef(); } - + + if (t == T_OBJ || t == T_ARR || t == T_FUNC || t == T_PROMISE) { + ant_value_t cur = obj; + sv_proto_guard_t guard; + sv_proto_guard_init(&guard); + while (is_object_type(cur)) { + ant_object_t *ptr = js_obj_ptr(js_as_obj(cur)); + bool should_fallback = false; + ant_value_t fast_out = js_mkundef(); + if (sv_try_get_shape_data_prop(js, ptr, str, &fast_out, &should_fallback)) + return fast_out; + if (should_fallback) break; + cur = ptr->proto; + if (sv_proto_guard_hit_cycle(&guard, cur)) break; + } + } + return sv_getprop_fallback_len(js, obj, str, (ant_offset_t)len); } @@ -102,7 +312,7 @@ static inline bool sv_parse_string_index_key(ant_t *js, ant_value_t key, size_t ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key, &klen); - const char *k = (const char *)&js->mem[koff]; + const char *k = (const char *)(uintptr_t)(koff); if (klen == 0) return false; if (klen > 1 && k[0] == '0') return false; @@ -121,7 +331,7 @@ static inline bool sv_parse_string_index_key(ant_t *js, ant_value_t key, size_t static inline bool sv_try_string_index_get(ant_t *js, ant_value_t obj, ant_value_t key, ant_value_t *out) { ant_value_t str = obj; if (vtype(obj) == T_OBJ) { - ant_value_t prim = js_get_slot(js, obj, SLOT_PRIMITIVE); + ant_value_t prim = js_get_slot(obj, SLOT_PRIMITIVE); if (vtype(prim) == T_STR) str = prim; } if (vtype(str) != T_STR) return false; @@ -131,7 +341,7 @@ static inline bool sv_try_string_index_get(ant_t *js, ant_value_t obj, ant_value ant_offset_t byte_len = 0; ant_offset_t str_off = vstr(js, str, &byte_len); - const char *str_data = (const char *)&js->mem[str_off]; + const char *str_data = (const char *)(uintptr_t)(str_off); size_t char_bytes = 0; int byte_offset = utf16_index_to_byte_offset(str_data, byte_len, idx, &char_bytes); if (byte_offset < 0 || char_bytes == 0) { @@ -143,6 +353,43 @@ static inline bool sv_try_string_index_get(ant_t *js, ant_value_t obj, ant_value return true; } +static inline ant_value_t sv_prop_get_field_ic( + ant_t *js, + ant_value_t obj, + sv_atom_t *a, + sv_func_t *func, + uint8_t *ip +) { + ant_object_t *ptr = is_object_type(obj) ? js_obj_ptr(js_as_obj(obj)) : NULL; + sv_ic_entry_t *ic = sv_ic_slot_for_ip(func, ip); + bool track_ic = ic && !is_length_key(a->str, a->len); + + ant_value_t hit = js_mkundef(); + if (ic && ptr && !ptr->is_exotic && track_ic && + sv_ic_try_get_hit(ic, ptr, a, &hit)) { + sv_gf_ic_note_success(ic); + return hit; + } + + if (ic && ptr && !ptr->is_exotic && track_ic) { + ant_object_t *holder = NULL; + uint32_t prop_idx = 0; + ant_value_t out = js_mkundef(); + if (sv_ic_probe_get_chain(obj, a->str, &holder, &prop_idx, &out)) { + ic->cached_shape = ptr->shape; + ic->cached_holder = holder; + ic->cached_index = prop_idx; + ic->cached_is_own = (holder == ptr); + ic->epoch = ant_ic_epoch_counter; + sv_gf_ic_note_success(ic); + return out; + } + } + + if (track_ic) sv_gf_ic_note_miss(ic); + return sv_prop_get_at(js, obj, a->str, a->len, func, ip); +} + static inline ant_value_t sv_op_get_field( sv_vm_t *vm, ant_t *js, sv_func_t *func, uint8_t *ip @@ -150,7 +397,7 @@ static inline ant_value_t sv_op_get_field( uint32_t idx = sv_get_u32(ip + 1); sv_atom_t *a = &func->atoms[idx]; ant_value_t obj = vm->stack[--vm->sp]; - ant_value_t res = sv_prop_get_at(js, obj, a->str, a->len, func, ip); + ant_value_t res = sv_prop_get_field_ic(js, obj, a, func, ip); if (is_err(res)) return res; vm->stack[vm->sp++] = res; return js_mkundef(); @@ -163,12 +410,77 @@ static inline ant_value_t sv_op_get_field2( uint32_t idx = sv_get_u32(ip + 1); sv_atom_t *a = &func->atoms[idx]; ant_value_t obj = vm->stack[vm->sp - 1]; - ant_value_t res = sv_prop_get_at(js, obj, a->str, a->len, func, ip); + ant_value_t res = sv_prop_get_field_ic(js, obj, a, func, ip); if (is_err(res)) return res; vm->stack[vm->sp++] = res; return js_mkundef(); } +static inline bool sv_try_put_field_fast( + ant_t *js, + ant_value_t obj, + sv_atom_t *a, + ant_value_t val, + uint32_t *out_index +) { + if (!is_object_type(obj)) return false; + + ant_object_t *ptr = js_obj_ptr(js_as_obj(obj)); + if (!ptr || ptr->is_exotic || !ptr->shape) return false; + if (ptr->type_tag == T_ARR && is_length_key(a->str, a->len)) return false; + + int32_t slot = ant_shape_lookup_interned(ptr->shape, a->str); + if (slot < 0) return false; + + uint32_t prop_idx = (uint32_t)slot; + if (prop_idx >= ptr->prop_count) return false; + + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, prop_idx); + if (!prop) return false; + if (prop->has_getter || prop->has_setter) return false; + if ((prop->attrs & ANT_PROP_ATTR_WRITABLE) == 0) return false; + + ant_object_prop_set_unchecked(ptr, prop_idx, val); + gc_write_barrier(js, ptr, val); + if (out_index) *out_index = prop_idx; + return true; +} + +static inline bool sv_is_proto_atom(const sv_atom_t *a) { + return a && a->len == STR_PROTO_LEN && memcmp(a->str, STR_PROTO, STR_PROTO_LEN) == 0; +} + +static inline void sv_ic_set_add_transition( + sv_ic_entry_t *ic, + ant_shape_t *from, + ant_shape_t *to, + uint32_t slot, + uint32_t epoch +) { + if (!ic) return; + + if (ic->add_from_shape != from) { + if (ic->add_from_shape) ant_shape_release(ic->add_from_shape); + ic->add_from_shape = NULL; + if (from) { + ant_shape_retain(from); + ic->add_from_shape = from; + } + } + + if (ic->add_to_shape != to) { + if (ic->add_to_shape) ant_shape_release(ic->add_to_shape); + ic->add_to_shape = NULL; + if (to) { + ant_shape_retain(to); + ic->add_to_shape = to; + } + } + + ic->add_slot = slot; + ic->add_epoch = epoch; +} + static inline ant_value_t sv_op_put_field( sv_vm_t *vm, ant_t *js, sv_func_t *func, uint8_t *ip @@ -177,8 +489,107 @@ static inline ant_value_t sv_op_put_field( sv_atom_t *a = &func->atoms[idx]; ant_value_t val = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[--vm->sp]; - ant_value_t key = js_mkstr(js, a->str, a->len); - return js_setprop(js, obj, key, val); + ant_object_t *ptr = is_object_type(obj) ? js_obj_ptr(js_as_obj(obj)) : NULL; + sv_ic_entry_t *ic = sv_ic_slot_for_ip(func, ip); + + if (ic && ptr && !ptr->is_exotic && ptr->shape && ic->epoch == ant_ic_epoch_counter && + ic->cached_shape == ptr->shape && ic->cached_holder == ptr && + ic->cached_index < ptr->prop_count) { + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, ic->cached_index); + if (prop && + prop->type == ANT_SHAPE_KEY_STRING && + prop->key.interned == a->str && + !prop->has_getter && + !prop->has_setter && + (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0) { + ant_object_prop_set_unchecked(ptr, ic->cached_index, val); + gc_write_barrier(js, ptr, val); + return val; + } + } + + if (ic && ptr && !ptr->is_exotic && ptr->shape && + !ptr->frozen && !ptr->sealed && ptr->extensible && + ptr->type_tag != T_ARR && + !sv_is_proto_atom(a) && + ic->add_epoch == ant_ic_epoch_counter && + ic->add_from_shape == ptr->shape && + ic->add_to_shape) { + if (ant_shape_lookup_interned(ptr->shape, a->str) < 0) { + ant_shape_t *old_shape = ptr->shape; + ant_shape_retain(ic->add_to_shape); + ptr->shape = ic->add_to_shape; + ant_shape_release(old_shape); + if (ic->add_slot >= ptr->prop_count && + !js_obj_ensure_prop_capacity(ptr, ic->add_slot + 1)) { + return js_mkerr(js, "oom"); + } + ant_object_prop_set_unchecked(ptr, ic->add_slot, val); + gc_write_barrier(js, ptr, val); + ic->cached_shape = ptr->shape; + ic->cached_holder = ptr; + ic->cached_index = ic->add_slot; + ic->epoch = ant_ic_epoch_counter; + return val; + } + } + + uint32_t fast_idx = 0; + if (sv_try_put_field_fast(js, obj, a, val, &fast_idx)) { + if (ic && ptr && ptr->shape) { + ic->cached_shape = ptr->shape; + ic->cached_holder = ptr; + ic->cached_index = fast_idx; + ic->epoch = ant_ic_epoch_counter; + } + return val; + } + + ant_shape_t *old_shape = NULL; + if (ptr && ptr->shape) { + old_shape = ptr->shape; + ant_shape_retain(old_shape); + } + + ant_value_t out = setprop_interned(js, obj, a->str, a->len, val); + if (is_err(out)) { + if (old_shape) ant_shape_release(old_shape); + return out; + } + + if (ic && ptr && !ptr->is_exotic && ptr->shape) { + int32_t slot = ant_shape_lookup_interned(ptr->shape, a->str); + if (slot >= 0) { + uint32_t prop_idx = (uint32_t)slot; + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, prop_idx); + if (prop && + prop->type == ANT_SHAPE_KEY_STRING && + prop->key.interned == a->str && + !prop->has_getter && + !prop->has_setter && + (prop->attrs & ANT_PROP_ATTR_WRITABLE) != 0 && + prop_idx < ptr->prop_count) { + ic->cached_shape = ptr->shape; + ic->cached_holder = ptr; + ic->cached_index = prop_idx; + ic->epoch = ant_ic_epoch_counter; + if (old_shape && + old_shape != ptr->shape && + !sv_is_proto_atom(a) && + !ptr->frozen && + !ptr->sealed && + ptr->extensible && + ptr->type_tag != T_ARR && + prop->attrs == ANT_PROP_ATTR_DEFAULT) { + sv_ic_set_add_transition(ic, old_shape, ptr->shape, prop_idx, ant_ic_epoch_counter); + } + } + } + } + + if (old_shape) ant_shape_release(old_shape); + + return out; } static inline ant_value_t sv_op_get_elem( @@ -258,6 +669,37 @@ static inline ant_value_t sv_op_put_elem(sv_vm_t *vm, ant_t *js) { return js_setprop(js, obj, key_jv, val); } +static inline bool sv_try_define_field_fast( + ant_t *js, + ant_value_t obj, + const char *interned_key, + ant_value_t val +) { + if (!interned_key || !is_object_type(obj)) return false; + + ant_value_t as_obj = js_as_obj(obj); + ant_object_t *ptr = js_obj_ptr(as_obj); + if (!ptr || ptr->is_exotic || !ptr->shape) return false; + if (ptr->type_tag == T_ARR) return false; + if (ptr->frozen || ptr->sealed || !ptr->extensible) return false; + + int32_t slot = ant_shape_lookup_interned(ptr->shape, interned_key); + if (slot >= 0) { + uint32_t idx = (uint32_t)slot; + if (idx >= ptr->prop_count) return false; + const ant_shape_prop_t *prop = ant_shape_prop_at(ptr->shape, idx); + if (!prop) return false; + if (prop->type != ANT_SHAPE_KEY_STRING || prop->key.interned != interned_key) return false; + if (prop->has_getter || prop->has_setter) return false; + if (prop->attrs != ANT_PROP_ATTR_DEFAULT) return false; + ant_object_prop_set_unchecked(ptr, idx, val); + gc_write_barrier(js, ptr, val); + return true; + } + + return !is_err(mkprop_interned(js, as_obj, interned_key, val, 0)); +} + static inline void sv_op_define_field( sv_vm_t *vm, ant_t *js, sv_func_t *func, uint8_t *ip @@ -266,7 +708,8 @@ static inline void sv_op_define_field( sv_atom_t *a = &func->atoms[idx]; ant_value_t val = vm->stack[--vm->sp]; ant_value_t obj = vm->stack[vm->sp - 1]; - js_define_own_prop(js, obj, a->str, a->len, val); + if (!sv_try_define_field_fast(js, obj, a->str, val)) + js_define_own_prop(js, obj, a->str, a->len, val); } static inline ant_value_t sv_op_get_length(sv_vm_t *vm, ant_t *js) { @@ -280,7 +723,7 @@ static inline ant_value_t sv_op_get_length(sv_vm_t *vm, ant_t *js) { if (vtype(obj) == T_STR) { ant_offset_t byte_len = 0; ant_offset_t off = vstr(js, obj, &byte_len); - const char *str_data = (const char *)&js->mem[off]; + const char *str_data = (const char *)(uintptr_t)(off); vm->stack[vm->sp++] = tov((double)(uint32_t)utf16_strlen(str_data, byte_len)); return js_mkundef(); } diff --git a/src/silver/ops/super.h b/src/silver/ops/super.h index 127c494..7b916c2 100644 --- a/src/silver/ops/super.h +++ b/src/silver/ops/super.h @@ -16,7 +16,7 @@ static inline void sv_op_get_super_val(sv_vm_t *vm, ant_t *js) { ant_value_t key_str = coerce_to_str(js, prop); ant_offset_t klen; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); vm->stack[vm->sp++] = js_getprop_super(js, proto, receiver, kptr); } diff --git a/src/silver/ops/unary.h b/src/silver/ops/unary.h index 75fdad7..1537669 100644 --- a/src/silver/ops/unary.h +++ b/src/silver/ops/unary.h @@ -36,7 +36,7 @@ static inline ant_value_t sv_op_delete(sv_vm_t *vm, ant_t *js) { if (!is_err(key_str) && vtype(key_str) == T_STR) { ant_offset_t klen = 0; ant_offset_t koff = vstr(js, key_str, &klen); - const char *kptr = (const char *)&js->mem[koff]; + const char *kptr = (const char *)(uintptr_t)(koff); ant_value_t result = js_delete_prop(js, obj, kptr, klen); if (is_err(result)) return result; vm->stack[vm->sp++] = result; diff --git a/src/silver/ops/upvalues.h b/src/silver/ops/upvalues.h index 3617587..b479fdc 100644 --- a/src/silver/ops/upvalues.h +++ b/src/silver/ops/upvalues.h @@ -11,36 +11,21 @@ static inline ant_value_t sv_setup_function_prototype( ant_value_t proto_obj = mkobj(js, 0); if (is_err(proto_obj)) return proto_obj; - ant_handle_t hproto = js_root(js, proto_obj); ant_value_t object_proto = js_get_ctor_proto(js, "Object", 6); - if (vtype(object_proto) == T_OBJ) - js_set_proto(js, js_deref(js, hproto), object_proto); + if (vtype(object_proto) == T_OBJ) js_set_proto_init(proto_obj, object_proto); ant_value_t ctor_key = js_mkstr(js, "constructor", 11); - if (is_err(ctor_key)) { - js_unroot(js, hproto); - return ctor_key; - } - ant_value_t set_ctor = js_setprop(js, js_deref(js, hproto), ctor_key, func_val); - if (is_err(set_ctor)) { - js_unroot(js, hproto); - return set_ctor; - } - js_set_descriptor(js, js_deref(js, hproto), "constructor", 11, JS_DESC_W | JS_DESC_C); + if (is_err(ctor_key)) return ctor_key; + ant_value_t set_ctor = js_setprop(js, proto_obj, ctor_key, func_val); + if (is_err(set_ctor)) return set_ctor; + js_set_descriptor(js, proto_obj, "constructor", 11, JS_DESC_W | JS_DESC_C); ant_value_t proto_key = js_mkstr(js, "prototype", 9); - if (is_err(proto_key)) { - js_unroot(js, hproto); - return proto_key; - } - ant_value_t set_proto = js_setprop(js, func_obj, proto_key, js_deref(js, hproto)); - if (is_err(set_proto)) { - js_unroot(js, hproto); - return set_proto; - } + if (is_err(proto_key)) return proto_key; + ant_value_t set_proto = js_setprop(js, func_obj, proto_key, proto_obj); + if (is_err(set_proto)) return set_proto; js_set_descriptor(js, func_obj, "prototype", 9, JS_DESC_W); - js_unroot(js, hproto); return js_mkundef(); } @@ -88,16 +73,13 @@ static inline void sv_op_close_upval(sv_vm_t *vm, sv_frame_t *frame, uint8_t *ip static inline sv_upvalue_t *sv_capture_upvalue(sv_vm_t *vm, ant_value_t *slot) { sv_upvalue_t **pp = &vm->open_upvalues; - while (*pp && (*pp)->location > slot) - pp = &(*pp)->next; - - if (*pp && (*pp)->location == slot) - return *pp; + + while (*pp && (*pp)->location > slot) pp = &(*pp)->next; + if (*pp && (*pp)->location == slot) return *pp; - sv_upvalue_t *uv = calloc(1, sizeof(sv_upvalue_t)); - uv->location = slot; - uv->next = *pp; - *pp = uv; + sv_upvalue_t *uv = js_upvalue_alloc(); + uv->location = slot; uv->next = *pp; *pp = uv; + return uv; } @@ -108,7 +90,7 @@ static inline void sv_op_closure( uint32_t idx = sv_get_u32(ip + 1); sv_func_t *child = (sv_func_t *)(uintptr_t)vdata(func->constants[idx]); - sv_closure_t *closure = calloc(1, sizeof(sv_closure_t)); + sv_closure_t *closure = js_closure_alloc(js); closure->func = child; closure->bound_this = child->is_arrow ? frame->this : js_mkundef(); closure->bound_args = js_mkundef(); @@ -129,26 +111,23 @@ static inline void sv_op_closure( ant_value_t func_obj = mkobj(js, 0); closure->func_obj = func_obj; + js_mark_constructor(func_obj, !child->is_arrow && !child->is_method); js_setprop(js, func_obj, js->length_str, tov((double)child->param_count)); js_set_descriptor(js, func_obj, "length", 6, JS_DESC_C); ant_value_t func_val = mkval(T_FUNC, (uintptr_t)closure); - if (!child->is_arrow && !child->is_method) { - (void)sv_setup_function_prototype(js, func_obj, func_val); - } + if (!child->is_arrow && !child->is_method) sv_setup_function_prototype(js, func_obj, func_val); if (child->is_strict) - js_set_slot(js, func_obj, SLOT_STRICT, js_true); + js_set_slot(func_obj, SLOT_STRICT, js_true); if (child->is_async) { - js_set_slot(js, func_obj, SLOT_ASYNC, js_true); - ant_value_t async_proto = js_get_slot(js, js->global, SLOT_ASYNC_PROTO); - if (vtype(async_proto) == T_FUNC) - js_set_proto(js, func_obj, async_proto); + js_set_slot(func_obj, SLOT_ASYNC, js_true); + ant_value_t async_proto = js_get_slot(js->global, SLOT_ASYNC_PROTO); + if (vtype(async_proto) == T_FUNC) js_set_proto_init(func_obj, async_proto); } else { - ant_value_t func_proto = js_get_slot(js, js->global, SLOT_FUNC_PROTO); - if (vtype(func_proto) == T_FUNC) - js_set_proto(js, func_obj, func_proto); + ant_value_t func_proto = js_get_slot(js->global, SLOT_FUNC_PROTO); + if (vtype(func_proto) == T_FUNC) js_set_proto_init(func_obj, func_proto); } vm->stack[vm->sp++] = func_val; diff --git a/src/silver/swarm.c b/src/silver/swarm.c index 5273955..c90dc25 100644 --- a/src/silver/swarm.c +++ b/src/silver/swarm.c @@ -7,9 +7,13 @@ #include "internal.h" #include "debug.h" +#include "shapes.h" +#pragma GCC diagnostic push +#pragma GCC diagnostic ignored "-Wmacro-redefined" #include #include +#pragma GCC diagnostic pop #include #include #include @@ -23,6 +27,7 @@ static const char *sv_op_name[] = { typedef struct { MIR_context_t ctx; + bool externals_loaded; } sv_jit_ctx_t; static sv_jit_ctx_t *jit_ctx_get(ant_t *js) { @@ -37,6 +42,68 @@ static void jit_ctx_remove(ant_t *js) { js->jit_ctx = NULL; } +static void jit_load_externals_once(sv_jit_ctx_t *jc) { + if (jc == NULL || jc->externals_loaded) return; + MIR_context_t ctx = jc->ctx; +#define LOAD_EXT(name) MIR_load_external(ctx, #name, name) + LOAD_EXT(jit_helper_add); + LOAD_EXT(jit_helper_sub); + LOAD_EXT(jit_helper_mul); + LOAD_EXT(jit_helper_div); + LOAD_EXT(jit_helper_mod); + LOAD_EXT(jit_helper_lt); + LOAD_EXT(jit_helper_le); + LOAD_EXT(jit_helper_gt); + LOAD_EXT(jit_helper_ge); + LOAD_EXT(jit_helper_call); + LOAD_EXT(jit_helper_apply); + LOAD_EXT(jit_helper_rest); + LOAD_EXT(jit_helper_tov); + LOAD_EXT(jit_helper_get_global); + LOAD_EXT(jit_helper_get_field); + LOAD_EXT(jit_helper_to_propkey); + LOAD_EXT(jit_helper_bailout_resume); + LOAD_EXT(jit_helper_close_upval); + LOAD_EXT(jit_helper_closure); + LOAD_EXT(jit_helper_in); + LOAD_EXT(jit_helper_instanceof); + LOAD_EXT(jit_helper_call_is_proto); + LOAD_EXT(jit_helper_get_length); + LOAD_EXT(jit_helper_define_field); + LOAD_EXT(jit_helper_seq); + LOAD_EXT(jit_helper_eq); + LOAD_EXT(jit_helper_ne); + LOAD_EXT(jit_helper_sne); + LOAD_EXT(jit_helper_put_field); + LOAD_EXT(jit_helper_get_elem); + LOAD_EXT(jit_helper_get_elem2); + LOAD_EXT(jit_helper_put_elem); + LOAD_EXT(jit_helper_put_global); + LOAD_EXT(jit_helper_object); + LOAD_EXT(jit_helper_array); + LOAD_EXT(jit_helper_catch_value); + LOAD_EXT(jit_helper_throw); + LOAD_EXT(jit_helper_throw_error); + LOAD_EXT(jit_helper_set_proto); + LOAD_EXT(jit_helper_band); + LOAD_EXT(jit_helper_bor); + LOAD_EXT(jit_helper_bxor); + LOAD_EXT(jit_helper_bnot); + LOAD_EXT(jit_helper_shl); + LOAD_EXT(jit_helper_shr); + LOAD_EXT(jit_helper_ushr); + LOAD_EXT(jit_helper_not); + LOAD_EXT(jit_helper_is_truthy); + LOAD_EXT(jit_helper_typeof); + LOAD_EXT(jit_helper_new); + LOAD_EXT(jit_helper_delete); + LOAD_EXT(jit_helper_set_name); + LOAD_EXT(jit_helper_stack_overflow); + LOAD_EXT(jit_helper_stack_overflow_error); +#undef LOAD_EXT + jc->externals_loaded = true; +} + void sv_jit_init(ant_t *js) { if (jit_ctx_get(js)) return; sv_jit_ctx_t *jc = calloc(1, sizeof(*jc)); @@ -44,6 +111,7 @@ void sv_jit_init(ant_t *js) { jc->ctx = MIR_init(); MIR_gen_init(jc->ctx); MIR_gen_set_optimize_level(jc->ctx, 2); + jit_load_externals_once(jc); jit_ctx_set(js, jc); } @@ -62,6 +130,8 @@ typedef struct { MIR_reg_t *d_regs; sv_func_t **known_func; uint8_t *slot_type; + uint64_t *known_const; + bool *has_const; int sp; int max; } jit_vstack_t; @@ -69,6 +139,14 @@ typedef struct { static MIR_reg_t vstack_push(jit_vstack_t *vs) { if (vs->known_func) vs->known_func[vs->sp] = NULL; if (vs->slot_type) vs->slot_type[vs->sp] = 0; + if (vs->has_const) vs->has_const[vs->sp] = false; + return vs->regs[vs->sp++]; +} + +static MIR_reg_t vstack_push_const(jit_vstack_t *vs, uint64_t val) { + if (vs->known_func) vs->known_func[vs->sp] = NULL; + if (vs->slot_type) vs->slot_type[vs->sp] = 0; + if (vs->has_const) { vs->has_const[vs->sp] = true; vs->known_const[vs->sp] = val; } return vs->regs[vs->sp++]; } @@ -282,10 +360,16 @@ static inline void mir_emit_self_tail( static inline bool jit_const_is_heap(ant_value_t cv) { uint8_t t = vtype(cv); - return ((1u << t) & GC_HEAP_TYPE_MASK) != 0; + return + ((1u << t) & ((1u << T_OBJ) + | (1u << T_STR) + | (1u << T_ARR) + | (1u << T_PROMISE) + | (1u << T_BIGINT) + | (1u << T_GENERATOR) + | (1u << T_SYMBOL))) != 0; } - static void mir_load_const_slot(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, ant_value_t *slot) { mir_load_imm(ctx, fn, dst, (uint64_t)(uintptr_t)slot); @@ -324,6 +408,9 @@ static void mir_emit_is_num_guard(MIR_context_t ctx, MIR_item_t fn, } #define NANBOX_TFUNC_TAG ((NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | (uint64_t)T_FUNC) +#define NANBOX_TOBJ_TAG ((NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | (uint64_t)T_OBJ) +#define NANBOX_TARR_TAG ((NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | (uint64_t)T_ARR) +#define NANBOX_TPROM_TAG ((NANBOX_PREFIX >> NANBOX_TYPE_SHIFT) | (uint64_t)T_PROMISE) static void mir_emit_get_closure(MIR_context_t ctx, MIR_item_t fn, MIR_reg_t dst, MIR_reg_t v, @@ -345,6 +432,291 @@ static void mir_emit_get_closure(MIR_context_t ctx, MIR_item_t fn, MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); } +static void mir_emit_value_to_objptr_or_jmp( + MIR_context_t ctx, MIR_item_t fn, + MIR_reg_t v, MIR_reg_t out_ptr, + MIR_reg_t r_tag, MIR_label_t slow +) { + MIR_label_t is_obj = MIR_new_label(ctx); + MIR_label_t is_func = MIR_new_label(ctx); + MIR_label_t done = MIR_new_label(ctx); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_tag), + MIR_new_reg_op(ctx, v), + MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_obj), + MIR_new_reg_op(ctx, r_tag), + MIR_new_uint_op(ctx, NANBOX_TOBJ_TAG))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_obj), + MIR_new_reg_op(ctx, r_tag), + MIR_new_uint_op(ctx, NANBOX_TARR_TAG))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_obj), + MIR_new_reg_op(ctx, r_tag), + MIR_new_uint_op(ctx, NANBOX_TPROM_TAG))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_func), + MIR_new_reg_op(ctx, r_tag), + MIR_new_uint_op(ctx, NANBOX_TFUNC_TAG))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, slow))); + + MIR_append_insn(ctx, fn, is_obj); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, out_ptr), + MIR_new_reg_op(ctx, v), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, done))); + + MIR_append_insn(ctx, fn, is_func); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, out_ptr), + MIR_new_reg_op(ctx, v), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, out_ptr), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_closure_t, func_obj), + out_ptr, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, out_ptr), + MIR_new_reg_op(ctx, out_ptr), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + MIR_append_insn(ctx, fn, done); +} + +static bool mir_emit_get_field_ic_fastpath( + MIR_context_t ctx, + MIR_item_t fn, + sv_func_t *func, + int bc_off, + uint16_t ic_idx, + sv_atom_t *atom, + MIR_reg_t obj, + MIR_reg_t dst, + MIR_label_t slow, + MIR_reg_t r_global_epoch +) { + if (!func || !func->ic_slots || !atom) return false; + if (ic_idx == UINT16_MAX || ic_idx >= func->ic_count) return false; + if (is_length_key(atom->str, atom->len)) return false; + + sv_ic_entry_t *ic = &func->ic_slots[ic_idx]; + if (!sv_gf_ic_active(ic->cached_aux)) return false; + char gf_ic_name[32], gf_ice_name[32]; + char gf_ot_name[32], gf_op_name[32], gf_os_name[32], gf_ics_name[32]; + char gf_h_name[32], gf_hs_name[32], gf_idx_name[32], gf_pc_name[32]; + char gf_ica_name[32], gf_il_name[32], gf_ovf_name[32], gf_ovi_name[32]; + char gf_io_name[32], gf_src_name[32]; + snprintf(gf_ic_name, sizeof(gf_ic_name), "gf_ic_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ice_name, sizeof(gf_ice_name), "gf_ice_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ot_name, sizeof(gf_ot_name), "gf_ot_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_op_name, sizeof(gf_op_name), "gf_op_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_os_name, sizeof(gf_os_name), "gf_os_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ics_name, sizeof(gf_ics_name), "gf_ics_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_h_name, sizeof(gf_h_name), "gf_h_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_hs_name, sizeof(gf_hs_name), "gf_hs_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_idx_name, sizeof(gf_idx_name), "gf_idx_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_pc_name, sizeof(gf_pc_name), "gf_pc_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ica_name, sizeof(gf_ica_name), "gf_ica_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_il_name, sizeof(gf_il_name), "gf_il_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ovf_name, sizeof(gf_ovf_name), "gf_ovf_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_ovi_name, sizeof(gf_ovi_name), "gf_ovi_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_io_name, sizeof(gf_io_name), "gf_io_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(gf_src_name, sizeof(gf_src_name), "gf_src_%d_%u", bc_off, (unsigned)ic_idx); + + MIR_reg_t r_ic = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ic_name); + MIR_reg_t r_ic_epoch = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ice_name); + MIR_reg_t r_obj_tag = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ot_name); + MIR_reg_t r_obj_ptr = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_op_name); + MIR_reg_t r_obj_shape = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_os_name); + MIR_reg_t r_ic_shape = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ics_name); + MIR_reg_t r_holder = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_h_name); + MIR_reg_t r_holder_shape = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_hs_name); + MIR_reg_t r_ic_idx_val = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_idx_name); + MIR_reg_t r_holder_prop_count = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_pc_name); + MIR_reg_t r_ic_aux = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ica_name); + MIR_reg_t r_inobj_limit = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_il_name); + MIR_reg_t r_overflow = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ovf_name); + MIR_reg_t r_overflow_idx = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_ovi_name); + MIR_reg_t r_is_own = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_io_name); + MIR_reg_t r_source = MIR_new_func_reg(ctx, fn->u.func, MIR_T_I64, gf_src_name); + + MIR_label_t load_overflow = MIR_new_label(ctx); + MIR_label_t fast_done = MIR_new_label(ctx); + MIR_label_t own_path = MIR_new_label(ctx); + MIR_label_t do_read = MIR_new_label(ctx); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)ic))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_aux), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_aux), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, r_ic_aux), + MIR_new_reg_op(ctx, r_ic_aux), + MIR_new_uint_op(ctx, (uint64_t)SV_GF_IC_AUX_ACTIVE_BIT))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_aux), + MIR_new_int_op(ctx, 0))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_mem_op(ctx, MIR_T_U32, + (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_global_epoch))); + + mir_emit_value_to_objptr_or_jmp( + ctx, fn, obj, r_obj_ptr, r_obj_tag, slow); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_obj_shape), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(ant_object_t, shape), r_obj_ptr, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_shape), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_shape), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_obj_shape), + MIR_new_reg_op(ctx, r_ic_shape))); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_is_own), + MIR_new_mem_op(ctx, MIR_T_U8, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_is_own), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, own_path), + MIR_new_reg_op(ctx, r_is_own), + MIR_new_int_op(ctx, 0))); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_holder), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_holder), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_holder), + MIR_new_int_op(ctx, 0))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_holder_shape), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(ant_object_t, shape), r_holder, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_holder_shape), + MIR_new_int_op(ctx, 0))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_source), + MIR_new_reg_op(ctx, r_holder))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, do_read))); + + MIR_append_insn(ctx, fn, own_path); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_source), + MIR_new_reg_op(ctx, r_obj_ptr))); + + MIR_append_insn(ctx, fn, do_read); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_mem_op(ctx, MIR_T_U32, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_index), r_ic, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_holder_prop_count), + MIR_new_mem_op(ctx, MIR_T_U32, + (MIR_disp_t)offsetof(ant_object_t, prop_count), r_source, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_UBGE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_reg_op(ctx, r_holder_prop_count))); + + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_inobj_limit), + MIR_new_mem_op(ctx, MIR_T_U8, + (MIR_disp_t)offsetof(ant_object_t, inobj_limit), r_source, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_UBGE, + MIR_new_label_op(ctx, load_overflow), + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_reg_op(ctx, r_inobj_limit))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, dst), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(ant_object_t, inobj), r_source, r_ic_idx_val, 8))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, fast_done))); + + MIR_append_insn(ctx, fn, load_overflow); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_overflow), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(ant_object_t, overflow_prop), r_source, 0, 1))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_overflow), + MIR_new_int_op(ctx, 0))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_SUB, + MIR_new_reg_op(ctx, r_overflow_idx), + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_reg_op(ctx, r_inobj_limit))); + MIR_append_insn(ctx, fn, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, dst), + MIR_new_mem_op(ctx, MIR_T_I64, 0, r_overflow, r_overflow_idx, 8))); + MIR_append_insn(ctx, fn, fast_done); + + return true; +} + #define MAX_OSR_ENTRIES 64 @@ -420,11 +792,10 @@ static bool *scan_captured_locals(sv_func_t *func, int n_locals) { } -#define JIT_INLINE_MAX_BYTECODE 64 +#define JIT_INLINE_MAX_BYTECODE 128 static bool jit_inlineable(sv_func_t *f) { if (!f) return false; - if (f->upvalue_count != 0) return false; if (f->is_async || f->is_generator) return false; if (f->code_len > JIT_INLINE_MAX_BYTECODE) return false; @@ -438,15 +809,29 @@ static bool jit_inlineable(sv_func_t *f) { case OP_GET_ARG: case OP_CONST_I8: case OP_CONST: case OP_CONST8: case OP_UNDEF: case OP_NULL: case OP_TRUE: case OP_FALSE: + case OP_THIS: case OP_GET_LOCAL: case OP_GET_LOCAL8: case OP_PUT_LOCAL: case OP_PUT_LOCAL8: case OP_SET_LOCAL: case OP_SET_LOCAL8: - case OP_POP: case OP_DUP: + case OP_GET_UPVAL: + case OP_POP: case OP_DUP: case OP_DUP2: + case OP_INSERT2: case OP_INSERT3: case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: - case OP_MOD: - case OP_LT: case OP_LE: + case OP_MOD: case OP_NEG: + case OP_LT: case OP_LE: case OP_GT: case OP_GE: + case OP_SEQ: case OP_SNE: case OP_EQ: case OP_NE: + case OP_IS_UNDEF: case OP_IS_NULL: + case OP_JMP: case OP_JMP_TRUE: case OP_JMP_FALSE: + case OP_JMP_TRUE8: case OP_JMP_FALSE8: + case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: case OP_RETURN: case OP_RETURN_UNDEF: + break; + case OP_GET_FIELD: case OP_GET_FIELD2: case OP_GET_GLOBAL: + break; + case OP_SPECIAL_OBJ: + if (sv_get_u8(ip + 1) != 1) return false; + break; case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: break; default: @@ -457,12 +842,72 @@ static bool jit_inlineable(sv_func_t *f) { return true; } +#define INL_MAX_LABELS 128 + +typedef struct { + int bc_off; + MIR_label_t label; + int sp; +} inl_label_entry_t; + +typedef struct { + inl_label_entry_t entries[INL_MAX_LABELS]; + int count; +} inl_label_map_t; + +static MIR_label_t inl_label_for_offset(MIR_context_t ctx, + inl_label_map_t *lm, + int bc_off, int sp) { + for (int i = 0; i < lm->count; i++) + if (lm->entries[i].bc_off == bc_off) { + if (lm->entries[i].sp < 0) lm->entries[i].sp = sp; + return lm->entries[i].label; + } + if (lm->count >= INL_MAX_LABELS) return NULL; + MIR_label_t lbl = MIR_new_label(ctx); + lm->entries[lm->count].bc_off = bc_off; + lm->entries[lm->count].label = lbl; + lm->entries[lm->count].sp = sp; + lm->count++; + return lbl; +} + +static MIR_label_t inl_label_lookup(inl_label_map_t *lm, int bc_off, int *out_sp) { + for (int i = 0; i < lm->count; i++) + if (lm->entries[i].bc_off == bc_off) { + if (out_sp) *out_sp = lm->entries[i].sp; + return lm->entries[i].label; + } + return NULL; +} + +static bool jit_inline_body_feasible(sv_func_t *callee) { + uint8_t *ip = callee->code; + uint8_t *end = callee->code + callee->code_len; + while (ip < end) { + sv_op_t op = (sv_op_t)*ip; + int sz = sv_op_size[op]; + if (sz == 0) return false; + switch (op) { + default: break; + } + ip += sz; + } + return true; +} + static bool jit_emit_inline_body( MIR_context_t ctx, MIR_item_t jit_func, sv_func_t *callee, MIR_reg_t *arg_regs, int caller_argc, MIR_reg_t result, MIR_label_t slow, MIR_label_t join, - MIR_reg_t r_bool, MIR_reg_t *p_d_slot, int id + MIR_reg_t r_bool, MIR_reg_t *p_d_slot, int id, + MIR_reg_t r_inl_closure, MIR_reg_t r_inl_this, + MIR_reg_t r_vm, MIR_reg_t r_js, + MIR_item_t helper2_proto, MIR_item_t imp_seq, + MIR_item_t imp_sne, MIR_item_t imp_eq, MIR_item_t imp_ne, + MIR_item_t gf_proto, MIR_item_t imp_get_field, + MIR_item_t gg_proto, MIR_item_t imp_gg ) { int inl_max_stack = callee->max_stack > 0 ? callee->max_stack : 4; MIR_reg_t inl_vs[inl_max_stack]; @@ -481,13 +926,25 @@ static bool jit_emit_inline_body( } int inl_arith = 0; + int inl_upval_n = 0; + inl_label_map_t inl_lm = {.count = 0}; + + uint8_t *code_base = callee->code; uint8_t *ip = callee->code; uint8_t *end = callee->code + callee->code_len; while (ip < end) { sv_op_t op = (sv_op_t)*ip; int sz = sv_op_size[op]; + int inl_bc_off = (int)(ip - code_base); + + int label_sp = -1; + MIR_label_t target_lbl = inl_label_lookup(&inl_lm, inl_bc_off, &label_sp); + if (target_lbl) { + MIR_append_insn(ctx, jit_func, target_lbl); + if (label_sp >= 0) isp = label_sp; + } switch (op) { case OP_GET_ARG: { @@ -536,6 +993,14 @@ static bool jit_emit_inline_body( case OP_TRUE: mir_load_imm(ctx, jit_func, inl_vs[isp++], js_true); break; case OP_FALSE: mir_load_imm(ctx, jit_func, inl_vs[isp++], js_false); break; + case OP_THIS: { + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp++]), + MIR_new_reg_op(ctx, r_inl_this))); + break; + } + case OP_GET_LOCAL: { uint16_t idx = sv_get_u16(ip + 1); if (idx >= (uint16_t)inl_n_locals) return false; @@ -591,6 +1056,45 @@ static bool jit_emit_inline_body( break; } + case OP_GET_UPVAL: { + if (!r_inl_closure) return false; + uint16_t idx = sv_get_u16(ip + 1); + int un = inl_upval_n++; + char rn_uvs[32], rn_uv[32], rn_loc[32]; + snprintf(rn_uvs, sizeof(rn_uvs), "inl%d_uvs%d", id, un); + snprintf(rn_uv, sizeof(rn_uv), "inl%d_uv%d", id, un); + snprintf(rn_loc, sizeof(rn_loc), "inl%d_uvl%d", id, un); + + MIR_reg_t r_uvs = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_uvs); + MIR_reg_t r_uv = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_uv); + MIR_reg_t r_loc = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, rn_loc); + MIR_reg_t dst = inl_vs[isp++]; + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_uvs), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(sv_closure_t, upvalues), + r_inl_closure, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_uv), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)((int)idx * (int)sizeof(sv_upvalue_t *)), + r_uvs, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_loc), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(sv_upvalue_t, location), + r_uv, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, dst), + MIR_new_mem_op(ctx, MIR_JSVAL, 0, r_loc, 0, 1))); + break; + } + case OP_POP: isp--; break; case OP_DUP: { MIR_append_insn(ctx, jit_func, @@ -600,6 +1104,77 @@ static bool jit_emit_inline_body( isp++; break; } + case OP_DUP2: { + if (isp < 2) return false; + MIR_reg_t ra = inl_vs[isp - 2]; + MIR_reg_t rb = inl_vs[isp - 1]; + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp]), + MIR_new_reg_op(ctx, ra))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp + 1]), + MIR_new_reg_op(ctx, rb))); + isp += 2; + break; + } + + case OP_INSERT2: { + if (isp < 2) return false; + char tn[32]; snprintf(tn, sizeof(tn), "inl%d_ins2t", id); + MIR_reg_t r_t = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, tn); + MIR_reg_t r_a = inl_vs[isp - 1]; + MIR_reg_t r_obj = inl_vs[isp - 2]; + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_t), + MIR_new_reg_op(ctx, r_a))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp - 1]), + MIR_new_reg_op(ctx, r_obj))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp - 2]), + MIR_new_reg_op(ctx, r_t))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp]), + MIR_new_reg_op(ctx, r_t))); + isp++; + break; + } + case OP_INSERT3: { + if (isp < 3) return false; + char tn[32]; snprintf(tn, sizeof(tn), "inl%d_ins3t", id); + MIR_reg_t r_t = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, tn); + MIR_reg_t r_a = inl_vs[isp - 1]; + MIR_reg_t r_prop = inl_vs[isp - 2]; + MIR_reg_t r_obj = inl_vs[isp - 3]; + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_t), + MIR_new_reg_op(ctx, r_a))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp - 1]), + MIR_new_reg_op(ctx, r_prop))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp - 2]), + MIR_new_reg_op(ctx, r_obj))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp - 3]), + MIR_new_reg_op(ctx, r_t))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, inl_vs[isp]), + MIR_new_reg_op(ctx, r_t))); + isp++; + break; + } case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: { @@ -652,48 +1227,339 @@ static bool jit_emit_inline_body( mir_d_to_i64(ctx, jit_func, rd, fd3, *p_d_slot); break; } - - case OP_LT: case OP_LE: { - MIR_reg_t rr = inl_vs[--isp]; - MIR_reg_t rl = inl_vs[--isp]; - MIR_reg_t rd = inl_vs[isp++]; - - mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); - mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow); - - if (!*p_d_slot) { - *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, - MIR_T_I64, "d_slot_inl"); - MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, MIR_ALLOCA, - MIR_new_reg_op(ctx, *p_d_slot), - MIR_new_uint_op(ctx, 8))); - } - - int cn = inl_arith++; - char cd1[32], cd2[32]; - snprintf(cd1, sizeof(cd1), "inl%d_cd1_%d", id, cn); - snprintf(cd2, sizeof(cd2), "inl%d_cd2_%d", id, cn); - MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd1); - MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd2); - mir_i64_to_d(ctx, jit_func, fd1, rl, *p_d_slot); - mir_i64_to_d(ctx, jit_func, fd2, rr, *p_d_slot); - - char cmp_rn[32]; - snprintf(cmp_rn, sizeof(cmp_rn), "inl%d_%s_%d", id, - op == OP_LT ? "lt" : "le", cn); - MIR_reg_t r_tmp = MIR_new_func_reg(ctx, jit_func->u.func, - MIR_T_I64, cmp_rn); - MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, op == OP_LT ? MIR_DLT : MIR_DLE, - MIR_new_reg_op(ctx, r_tmp), - MIR_new_reg_op(ctx, fd1), - MIR_new_reg_op(ctx, fd2))); + + case OP_MOD: { + mir_emit_is_num_guard(ctx, jit_func, r_bool, inl_vs[isp - 1], slow); + mir_emit_is_num_guard(ctx, jit_func, r_bool, inl_vs[isp - 2], slow); + + if (!*p_d_slot) { + *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, "d_slot_inl"); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_ALLOCA, + MIR_new_reg_op(ctx, *p_d_slot), + MIR_new_uint_op(ctx, 8))); + } + + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + + int mn = inl_arith++; + char md1[32], md2[32], md3[32], md4[32], md5[32]; + snprintf(md1, sizeof(md1), "inl%d_mod1_%d", id, mn); + snprintf(md2, sizeof(md2), "inl%d_mod2_%d", id, mn); + snprintf(md3, sizeof(md3), "inl%d_mod3_%d", id, mn); + snprintf(md4, sizeof(md4), "inl%d_mod4_%d", id, mn); + snprintf(md5, sizeof(md5), "inl%d_mod5_%d", id, mn); + MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, md1); + MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, md2); + MIR_reg_t fd3 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, md3); + MIR_reg_t fd4 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, md4); + MIR_reg_t fd5 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, md5); + + mir_i64_to_d(ctx, jit_func, fd1, rl, *p_d_slot); + mir_i64_to_d(ctx, jit_func, fd2, rr, *p_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DDIV, + MIR_new_reg_op(ctx, fd3), + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, fd2))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_D2I, + MIR_new_reg_op(ctx, rd), + MIR_new_reg_op(ctx, fd3))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_I2D, + MIR_new_reg_op(ctx, fd4), + MIR_new_reg_op(ctx, rd))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMUL, + MIR_new_reg_op(ctx, fd4), + MIR_new_reg_op(ctx, fd4), + MIR_new_reg_op(ctx, fd2))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DSUB, + MIR_new_reg_op(ctx, fd5), + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, fd4))); + mir_d_to_i64(ctx, jit_func, rd, fd5, *p_d_slot); + break; + } + + case OP_NEG: { + MIR_reg_t rs = inl_vs[isp - 1]; + mir_emit_is_num_guard(ctx, jit_func, r_bool, rs, slow); + + if (!*p_d_slot) { + *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, "d_slot_inl"); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_ALLOCA, + MIR_new_reg_op(ctx, *p_d_slot), + MIR_new_uint_op(ctx, 8))); + } + + int nn = inl_arith++; + char nd1[32], nd2[32]; + snprintf(nd1, sizeof(nd1), "inl%d_neg1_%d", id, nn); + snprintf(nd2, sizeof(nd2), "inl%d_neg2_%d", id, nn); + MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, nd1); + MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, nd2); + mir_i64_to_d(ctx, jit_func, fd1, rs, *p_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DNEG, + MIR_new_reg_op(ctx, fd2), + MIR_new_reg_op(ctx, fd1))); + mir_d_to_i64(ctx, jit_func, rs, fd2, *p_d_slot); + break; + } + + case OP_LT: case OP_LE: case OP_GT: case OP_GE: { + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + + mir_emit_is_num_guard(ctx, jit_func, r_bool, rl, slow); + mir_emit_is_num_guard(ctx, jit_func, r_bool, rr, slow); + + if (!*p_d_slot) { + *p_d_slot = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, "d_slot_inl"); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_ALLOCA, + MIR_new_reg_op(ctx, *p_d_slot), + MIR_new_uint_op(ctx, 8))); + } + + int cn = inl_arith++; + char cd1[32], cd2[32]; + snprintf(cd1, sizeof(cd1), "inl%d_cd1_%d", id, cn); + snprintf(cd2, sizeof(cd2), "inl%d_cd2_%d", id, cn); + MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd1); + MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, cd2); + mir_i64_to_d(ctx, jit_func, fd1, rl, *p_d_slot); + mir_i64_to_d(ctx, jit_func, fd2, rr, *p_d_slot); + + MIR_insn_code_t cmp_op; + const char *cmp_name; + switch (op) { + case OP_LT: cmp_op = MIR_DLT; cmp_name = "lt"; break; + case OP_LE: cmp_op = MIR_DLE; cmp_name = "le"; break; + case OP_GT: cmp_op = MIR_DGT; cmp_name = "gt"; break; + default: cmp_op = MIR_DGE; cmp_name = "ge"; break; + } + + char cmp_rn[32]; + snprintf(cmp_rn, sizeof(cmp_rn), "inl%d_%s_%d", id, cmp_name, cn); + MIR_reg_t r_tmp = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, cmp_rn); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, cmp_op, + MIR_new_reg_op(ctx, r_tmp), + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, fd2))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_OR, + MIR_new_reg_op(ctx, rd), + MIR_new_uint_op(ctx, js_false), + MIR_new_reg_op(ctx, r_tmp))); + break; + } + + case OP_SEQ: { + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + mir_call_helper2(ctx, jit_func, rd, + helper2_proto, imp_seq, + r_vm, r_js, rl, rr); + break; + } + case OP_SNE: { + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + mir_call_helper2(ctx, jit_func, rd, + helper2_proto, imp_sne, + r_vm, r_js, rl, rr); + break; + } + case OP_EQ: { + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + mir_call_helper2(ctx, jit_func, rd, + helper2_proto, imp_eq, + r_vm, r_js, rl, rr); + break; + } + case OP_NE: { + MIR_reg_t rr = inl_vs[--isp]; + MIR_reg_t rl = inl_vs[--isp]; + MIR_reg_t rd = inl_vs[isp++]; + mir_call_helper2(ctx, jit_func, rd, + helper2_proto, imp_ne, + r_vm, r_js, rl, rr); + break; + } + + case OP_IS_UNDEF: case OP_IS_NULL: { + MIR_reg_t rs = inl_vs[isp - 1]; + uint64_t cmp_val = (op == OP_IS_UNDEF) + ? mkval(T_UNDEF, 0) : mkval(T_NULL, 0); + MIR_label_t is_true = MIR_new_label(ctx); + MIR_label_t is_done = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_true), + MIR_new_reg_op(ctx, rs), + MIR_new_uint_op(ctx, cmp_val))); + mir_load_imm(ctx, jit_func, rs, js_false); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); + MIR_append_insn(ctx, jit_func, is_true); + mir_load_imm(ctx, jit_func, rs, js_true); + MIR_append_insn(ctx, jit_func, is_done); + break; + } + + case OP_JMP: { + int target = inl_bc_off + sz + sv_get_i32(ip + 1); + MIR_label_t lbl = inl_label_for_offset(ctx, &inl_lm, target, isp); + if (!lbl) return false; + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, lbl))); + break; + } + + case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE_PEEK: + case OP_JMP_TRUE: case OP_JMP_FALSE: + case OP_JMP_TRUE8: case OP_JMP_FALSE8: { + bool is_peek = (op == OP_JMP_TRUE_PEEK || op == OP_JMP_FALSE_PEEK); + MIR_reg_t cond = is_peek ? inl_vs[isp - 1] : inl_vs[--isp]; + bool short_op = (op == OP_JMP_TRUE8 || op == OP_JMP_FALSE8); + bool is_false_branch = (op == OP_JMP_FALSE || op == OP_JMP_FALSE8 + || op == OP_JMP_FALSE_PEEK); + int target = inl_bc_off + sz + (short_op ? (int8_t)sv_get_i8(ip + 1) + : sv_get_i32(ip + 1)); + MIR_label_t lbl = inl_label_for_offset(ctx, &inl_lm, target, isp); + if (!lbl) return false; + + uint64_t cmp_bool = is_false_branch ? js_false : js_true; + MIR_label_t lbl_not_bool = MIR_new_label(ctx); + MIR_label_t lbl_done = MIR_new_label(ctx); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, cond), + MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, lbl_not_bool), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, js_false >> NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, lbl), + MIR_new_reg_op(ctx, cond), + MIR_new_uint_op(ctx, cmp_bool))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, lbl_done))); + + MIR_append_insn(ctx, jit_func, lbl_not_bool); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_UBGT, + MIR_new_label_op(ctx, is_false_branch ? lbl_done : lbl), + MIR_new_reg_op(ctx, cond), + MIR_new_uint_op(ctx, NANBOX_PREFIX))); + if (is_false_branch) { + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, lbl_done))); + } else { + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, lbl_done))); + } + + MIR_append_insn(ctx, jit_func, lbl_done); + break; + } + + case OP_GET_FIELD: { + uint32_t idx = sv_get_u32(ip + 1); + if (idx >= (uint32_t)callee->atom_count) return false; + sv_atom_t *atom = &callee->atoms[idx]; + MIR_reg_t obj = inl_vs[--isp]; + MIR_reg_t dst = inl_vs[isp++]; + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 10, + MIR_new_ref_op(ctx, gf_proto), + MIR_new_ref_op(ctx, imp_get_field), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, obj), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom->str), + MIR_new_uint_op(ctx, (uint64_t)atom->len), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), + MIR_new_int_op(ctx, (int64_t)inl_bc_off))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, dst), + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, JIT_ERR_TAG))); + break; + } + case OP_GET_FIELD2: { + uint32_t idx = sv_get_u32(ip + 1); + if (idx >= (uint32_t)callee->atom_count) return false; + sv_atom_t *atom = &callee->atoms[idx]; + MIR_reg_t obj = inl_vs[isp - 1]; + MIR_reg_t dst = inl_vs[isp++]; + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 10, + MIR_new_ref_op(ctx, gf_proto), + MIR_new_ref_op(ctx, imp_get_field), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, obj), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom->str), + MIR_new_uint_op(ctx, (uint64_t)atom->len), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), + MIR_new_int_op(ctx, (int64_t)inl_bc_off))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, dst), + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, JIT_ERR_TAG))); + break; + } + case OP_GET_GLOBAL: { + uint32_t idx = sv_get_u32(ip + 1); + if (idx >= (uint32_t)callee->atom_count) return false; + sv_atom_t *atom = &callee->atoms[idx]; + MIR_reg_t dst = inl_vs[isp++]; MIR_append_insn(ctx, jit_func, - MIR_new_insn(ctx, MIR_OR, - MIR_new_reg_op(ctx, rd), - MIR_new_uint_op(ctx, js_false), - MIR_new_reg_op(ctx, r_tmp))); + MIR_new_call_insn(ctx, 7, + MIR_new_ref_op(ctx, gg_proto), + MIR_new_ref_op(ctx, imp_gg), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_js), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom->str), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)callee), + MIR_new_int_op(ctx, (int64_t)inl_bc_off))); break; } @@ -713,6 +1579,10 @@ static bool jit_emit_inline_body( MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, join))); break; + case OP_SPECIAL_OBJ: + mir_load_imm(ctx, jit_func, inl_vs[isp++], mkval(T_UNDEF, 0)); + break; + case OP_NOP: case OP_LINE_NUM: case OP_COL_NUM: case OP_LABEL: break; @@ -767,6 +1637,7 @@ typedef struct { bool needs_close_upval; bool needs_args_norm; bool needs_tco_args; + bool needs_ic_epoch; } jit_features_t; static jit_features_t jit_prescan_features(sv_func_t *func) { @@ -789,11 +1660,13 @@ static jit_features_t jit_prescan_features(sv_func_t *func) { f.needs_bailout = true; break; case OP_INC_LOCAL: case OP_DEC_LOCAL: + case OP_POST_INC: f.needs_inc_local = true; break; case OP_CALL: case OP_CALL_METHOD: case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: case OP_ARRAY: case OP_NEW: + case OP_APPLY: f.needs_args_buf = true; if (op == OP_TAIL_CALL || op == OP_TAIL_CALL_METHOD) f.needs_tco_args = true; @@ -805,6 +1678,10 @@ static jit_features_t jit_prescan_features(sv_func_t *func) { case OP_SET_ARG: f.needs_args_norm = true; break; + case OP_GET_FIELD: case OP_GET_FIELD2: case OP_PUT_FIELD: + case OP_INSTANCEOF: case OP_CALL_IS_PROTO: + f.needs_ic_epoch = true; + break; default: break; } ip += sz; @@ -833,10 +1710,12 @@ static bool jit_is_eligible(sv_func_t *func) { case OP_SET_LOCAL_UNDEF: case OP_GET_UPVAL: case OP_PUT_UPVAL: case OP_SET_UPVAL: case OP_CLOSE_UPVAL: + case OP_REST: case OP_POP: case OP_DUP: case OP_DUP2: case OP_INSERT2: case OP_INSERT3: case OP_ADD: case OP_SUB: case OP_MUL: case OP_DIV: case OP_MOD: case OP_ADD_NUM: case OP_SUB_NUM: case OP_MUL_NUM: case OP_DIV_NUM: + case OP_POST_INC: case OP_NEG: case OP_IS_UNDEF: case OP_IS_NULL: case OP_LT: case OP_LE: case OP_GT: case OP_GE: case OP_NE: case OP_SNE: @@ -849,8 +1728,11 @@ static bool jit_is_eligible(sv_func_t *func) { case OP_JMP: case OP_JMP8: case OP_JMP_FALSE: case OP_JMP_FALSE8: case OP_JMP_TRUE: case OP_JMP_TRUE8: + case OP_JMP_FALSE_PEEK: case OP_JMP_TRUE_PEEK: case OP_CALL: case OP_CALL_METHOD: + case OP_CALL_IS_PROTO: case OP_TAIL_CALL: case OP_TAIL_CALL_METHOD: + case OP_APPLY: case OP_GET_GLOBAL: case OP_GET_GLOBAL_UNDEF: case OP_PUT_GLOBAL: case OP_GET_FIELD: case OP_GET_FIELD2: case OP_PUT_FIELD: @@ -892,17 +1774,19 @@ static bool jit_is_eligible(sv_func_t *func) { } sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_closure) { - if (func->jit_compile_failed) return NULL; + if (func->jit_compile_failed || func->jit_compiling) return NULL; if (func->jit_code == NULL && func->jit_compiled_tfb_ver != 0 && func->tfb_version == func->jit_compiled_tfb_ver) { func->jit_compile_failed = true; return NULL; } if (!jit_is_eligible(func)) { func->jit_compile_failed = true; return NULL; } + func->jit_compiling = true; sv_jit_ctx_t *jc = jit_ctx_get(js); if (!jc) { sv_jit_init(js); jc = jit_ctx_get(js); } - if (!jc) return NULL; + if (!jc) { func->jit_compiling = false; return NULL; } + jit_load_externals_once(jc); MIR_context_t ctx = jc->ctx; @@ -944,10 +1828,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_type_t gg_ret = MIR_JSVAL; MIR_item_t gg_proto = MIR_new_proto(ctx, "gg_proto", - 1, &gg_ret, 3, + 1, &gg_ret, 4, MIR_T_I64, "js", MIR_T_P, "str", - MIR_T_I32, "len"); + MIR_T_P, "func", + MIR_T_I32, "bc_off"); + + MIR_type_t rest_ret = MIR_JSVAL; + MIR_item_t rest_proto = MIR_new_proto(ctx, "rest_proto", + 1, &rest_ret, 5, + MIR_T_I64, "vm", + MIR_T_I64, "js", + MIR_T_P, "args", + MIR_T_I32, "argc", + MIR_T_I32, "start"); MIR_type_t gf_ret = MIR_JSVAL; MIR_item_t gf_proto = MIR_new_proto(ctx, "gf_proto", @@ -970,6 +1864,27 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_T_P, "func", MIR_T_I32, "bc_off"); + MIR_type_t inst_ret = MIR_JSVAL; + MIR_item_t inst_proto = MIR_new_proto(ctx, "inst_proto", + 1, &inst_ret, 6, + MIR_T_I64, "vm", + MIR_T_I64, "js", + MIR_JSVAL, "l", + MIR_JSVAL, "r", + MIR_T_P, "func", + MIR_T_I32, "bc_off"); + + MIR_type_t cip_ret = MIR_JSVAL; + MIR_item_t call_is_proto = MIR_new_proto(ctx, "cip_proto", + 1, &cip_ret, 7, + MIR_T_I64, "vm", + MIR_T_I64, "js", + MIR_JSVAL, "this_val", + MIR_JSVAL, "func_val", + MIR_JSVAL, "arg", + MIR_T_P, "func", + MIR_T_I32, "bc_off"); + MIR_type_t h1_ret = MIR_JSVAL; MIR_item_t helper1_proto = MIR_new_proto(ctx, "helper1_proto", 1, &h1_ret, 3, @@ -1107,6 +2022,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_T_P, "args", MIR_T_I32, "argc"); + MIR_item_t imp_add = MIR_new_import(ctx, "jit_helper_add"); MIR_item_t imp_sub = MIR_new_import(ctx, "jit_helper_sub"); MIR_item_t imp_mul = MIR_new_import(ctx, "jit_helper_mul"); @@ -1117,6 +2033,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_item_t imp_gt = MIR_new_import(ctx, "jit_helper_gt"); MIR_item_t imp_ge = MIR_new_import(ctx, "jit_helper_ge"); MIR_item_t imp_call = MIR_new_import(ctx, "jit_helper_call"); + MIR_item_t imp_apply = MIR_new_import(ctx, "jit_helper_apply"); + MIR_item_t imp_rest = MIR_new_import(ctx, "jit_helper_rest"); MIR_item_t imp_tov = MIR_new_import(ctx, "jit_helper_tov"); MIR_item_t imp_gg = MIR_new_import(ctx, "jit_helper_get_global"); MIR_item_t imp_get_field = MIR_new_import(ctx, "jit_helper_get_field"); @@ -1154,6 +2072,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_item_t imp_typeof = MIR_new_import(ctx, "jit_helper_typeof"); MIR_item_t imp_new = MIR_new_import(ctx, "jit_helper_new"); MIR_item_t imp_instanceof = MIR_new_import(ctx, "jit_helper_instanceof"); + MIR_item_t imp_call_is_proto = MIR_new_import(ctx, "jit_helper_call_is_proto"); MIR_item_t imp_delete = MIR_new_import(ctx, "jit_helper_delete"); MIR_item_t imp_set_name = MIR_new_import(ctx, "jit_helper_set_name"); MIR_item_t imp_stack_ovf = MIR_new_import(ctx, "jit_helper_stack_overflow"); @@ -1214,9 +2133,12 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos vs.known_func = calloc((size_t)vs.max, sizeof(sv_func_t *)); vs.d_regs = calloc((size_t)vs.max, sizeof(MIR_reg_t)); vs.slot_type = calloc((size_t)vs.max, sizeof(uint8_t)); - if (!vs.regs || !vs.known_func || !vs.d_regs || !vs.slot_type) { + vs.known_const = calloc((size_t)vs.max, sizeof(uint64_t)); + vs.has_const = calloc((size_t)vs.max, sizeof(bool)); + if (!vs.regs || !vs.known_func || !vs.d_regs || !vs.slot_type || !vs.known_const || !vs.has_const) { free(vs.regs); free(vs.known_func); free(vs.d_regs); free(vs.slot_type); - MIR_finish_func(ctx); MIR_finish_module(ctx); return NULL; + free(vs.known_const); free(vs.has_const); + MIR_finish_func(ctx); MIR_finish_module(ctx); func->jit_compiling = false; return NULL; } for (int i = 0; i < vs.max; i++) { @@ -1232,15 +2154,18 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos int n_locals = func->max_locals; MIR_reg_t *local_regs = NULL; + MIR_reg_t *local_d_regs = NULL; sv_func_t **known_func_locals = NULL; uint8_t *known_type_locals = NULL; if (n_locals > 0) { local_regs = calloc((size_t)n_locals, sizeof(MIR_reg_t)); + local_d_regs = calloc((size_t)n_locals, sizeof(MIR_reg_t)); known_func_locals = calloc((size_t)n_locals, sizeof(sv_func_t *)); known_type_locals = calloc((size_t)n_locals, sizeof(uint8_t)); - if (!local_regs || !known_func_locals || !known_type_locals) { + if (!local_regs || !local_d_regs || !known_func_locals || !known_type_locals) { free(vs.regs); free(vs.known_func); free(vs.d_regs); free(vs.slot_type); - free(local_regs); free(known_func_locals); free(known_type_locals); + free(vs.known_const); free(vs.has_const); + free(local_regs); free(local_d_regs); free(known_func_locals); free(known_type_locals); MIR_finish_func(ctx); MIR_finish_module(ctx); return NULL; } if (func->local_types && func->local_type_count > 0) { @@ -1248,10 +2173,19 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos for (int i = 0; i < ncopy; i++) known_type_locals[i] = func->local_types[i].type; } + if (func->local_type_feedback) { + for (int i = 0; i < n_locals; i++) { + uint8_t ltf = func->local_type_feedback[i]; + if (ltf && !(ltf & ~SV_TFB_NUM)) + known_type_locals[i] = SV_TI_NUM; + } + } for (int i = 0; i < n_locals; i++) { - char rname[32]; + char rname[32], dname[32]; snprintf(rname, sizeof(rname), "l%d", i); + snprintf(dname, sizeof(dname), "ld%d", i); local_regs[i] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, rname); + local_d_regs[i] = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, dname); mir_load_imm(ctx, jit_func, local_regs[i], mkval(T_UNDEF, 0)); } @@ -1296,6 +2230,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_reg_t r_cond_d = 0, r_cond_nan = 0, r_cond_zd = 0, r_cond_zero = 0; bool needs_bailout = feat.needs_bailout; + MIR_reg_t r_ic_epoch_val = 0; + if (feat.needs_ic_epoch) { + r_ic_epoch_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_epoch"); + MIR_reg_t r_ep_tmp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "ic_ep_ptr"); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ep_tmp), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)&ant_ic_epoch_counter))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_epoch_val), + MIR_new_mem_op(ctx, MIR_T_U32, 0, r_ep_tmp, 0, 1))); + } + MIR_reg_t r_bailout_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_JSVAL, "bail_val"); MIR_reg_t r_bailout_off = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "bail_off"); MIR_reg_t r_bailout_sp = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, "bail_sp"); @@ -1497,6 +2445,14 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos vs.sp = lm.entries[i].sp; if (vs.slot_type) memset(vs.slot_type, SLOT_BOXED, (size_t)vs.max); + if (vs.has_const) + memset(vs.has_const, 0, (size_t)vs.max * sizeof(bool)); + if (known_type_locals && local_d_regs) { + for (int li = 0; li < n_locals; li++) + if (known_type_locals[li] == SV_TI_NUM) + mir_i64_to_d(ctx, jit_func, local_d_regs[li], + local_regs[li], r_d_slot); + } } } @@ -1558,16 +2514,16 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } case OP_UNDEF: - mir_load_imm(ctx, jit_func, vstack_push(&vs), mkval(T_UNDEF, 0)); + mir_load_imm(ctx, jit_func, vstack_push_const(&vs, mkval(T_UNDEF, 0)), mkval(T_UNDEF, 0)); break; case OP_NULL: - mir_load_imm(ctx, jit_func, vstack_push(&vs), mkval(T_NULL, 0)); + mir_load_imm(ctx, jit_func, vstack_push_const(&vs, mkval(T_NULL, 0)), mkval(T_NULL, 0)); break; case OP_TRUE: - mir_load_imm(ctx, jit_func, vstack_push(&vs), js_true); + mir_load_imm(ctx, jit_func, vstack_push_const(&vs, js_true), js_true); break; case OP_FALSE: - mir_load_imm(ctx, jit_func, vstack_push(&vs), js_false); + mir_load_imm(ctx, jit_func, vstack_push_const(&vs, js_false), js_false); break; case OP_THIS: { @@ -1604,6 +2560,22 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } + case OP_REST: { + uint16_t start = sv_get_u16(ip + 1); + MIR_reg_t dst = vstack_push(&vs); + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 8, + MIR_new_ref_op(ctx, rest_proto), + MIR_new_ref_op(ctx, imp_rest), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, r_args), + MIR_new_reg_op(ctx, r_argc), + MIR_new_int_op(ctx, (int64_t)start))); + break; + } + case OP_GET_LOCAL: { uint16_t idx = sv_get_u16(ip + 1); if (idx >= (uint16_t)n_locals) { ok = false; break; } @@ -1620,7 +2592,10 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, local_regs[idx]))); if (known_type_locals && known_type_locals[idx] == SV_TI_NUM) { - mir_i64_to_d(ctx, jit_func, vs.d_regs[vs.sp - 1], dst, r_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, vs.d_regs[vs.sp - 1]), + MIR_new_reg_op(ctx, local_d_regs[idx]))); if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_NUM; } break; @@ -1641,7 +2616,10 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, local_regs[idx]))); if (known_type_locals && known_type_locals[idx] == SV_TI_NUM) { - mir_i64_to_d(ctx, jit_func, vs.d_regs[vs.sp - 1], dst, r_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, vs.d_regs[vs.sp - 1]), + MIR_new_reg_op(ctx, local_d_regs[idx]))); if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_NUM; } break; @@ -1651,14 +2629,24 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint16_t idx = sv_get_u16(ip + 1); if (idx >= (uint16_t)n_locals) { ok = false; break; } sv_func_t *kf = vs.known_func[vs.sp - 1]; + bool src_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; + MIR_reg_t src_d = src_is_num ? vs.d_regs[vs.sp - 1] : 0; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t src = vstack_pop(&vs); if (known_func_locals) known_func_locals[idx] = kf; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, local_regs[idx]), MIR_new_reg_op(ctx, src))); + if (local_d_regs && known_type_locals && known_type_locals[idx] == SV_TI_NUM) { + if (src_is_num) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, src_d))); + else + mir_i64_to_d(ctx, jit_func, local_d_regs[idx], local_regs[idx], r_d_slot); + } if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, @@ -1671,14 +2659,24 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint8_t idx = sv_get_u8(ip + 1); if (idx >= (uint8_t)n_locals) { ok = false; break; } sv_func_t *kf = vs.known_func[vs.sp - 1]; + bool src_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; + MIR_reg_t src_d = src_is_num ? vs.d_regs[vs.sp - 1] : 0; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t src = vstack_pop(&vs); if (known_func_locals) known_func_locals[idx] = kf; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, local_regs[idx]), MIR_new_reg_op(ctx, src))); + if (local_d_regs && known_type_locals && known_type_locals[idx] == SV_TI_NUM) { + if (src_is_num) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, src_d))); + else + mir_i64_to_d(ctx, jit_func, local_d_regs[idx], local_regs[idx], r_d_slot); + } if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, @@ -1691,14 +2689,24 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_SET_LOCAL: { uint16_t idx = sv_get_u16(ip + 1); if (idx >= (uint16_t)n_locals) { ok = false; break; } + bool src_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; + MIR_reg_t src_d = src_is_num ? vs.d_regs[vs.sp - 1] : 0; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t src = vstack_top(&vs); if (known_func_locals) known_func_locals[idx] = vs.known_func[vs.sp - 1]; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, local_regs[idx]), MIR_new_reg_op(ctx, src))); + if (local_d_regs && known_type_locals && known_type_locals[idx] == SV_TI_NUM) { + if (src_is_num) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, src_d))); + else + mir_i64_to_d(ctx, jit_func, local_d_regs[idx], local_regs[idx], r_d_slot); + } if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, @@ -1710,14 +2718,24 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_SET_LOCAL8: { uint8_t idx = sv_get_u8(ip + 1); if (idx >= (uint8_t)n_locals) { ok = false; break; } + bool src_is_num = vs.slot_type && vs.slot_type[vs.sp - 1] == SLOT_NUM; + MIR_reg_t src_d = src_is_num ? vs.d_regs[vs.sp - 1] : 0; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t src = vstack_top(&vs); if (known_func_locals) known_func_locals[idx] = vs.known_func[vs.sp - 1]; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, local_regs[idx]), MIR_new_reg_op(ctx, src))); + if (local_d_regs && known_type_locals && known_type_locals[idx] == SV_TI_NUM) { + if (src_is_num) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, src_d))); + else + mir_i64_to_d(ctx, jit_func, local_d_regs[idx], local_regs[idx], r_d_slot); + } if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, @@ -1740,10 +2758,13 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_DUP: { sv_func_t *kf = vs.known_func[vs.sp - 1]; + bool kc = vs.has_const && vs.has_const[vs.sp - 1]; + uint64_t kcv = kc ? vs.known_const[vs.sp - 1] : 0; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t top = vstack_top(&vs); MIR_reg_t dst = vstack_push(&vs); vs.known_func[vs.sp - 1] = kf; + if (kc && vs.has_const) { vs.has_const[vs.sp - 1] = true; vs.known_const[vs.sp - 1] = kcv; } MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_MOV, MIR_new_reg_op(ctx, dst), @@ -2556,6 +3577,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } case OP_NEG: { + if (vs.slot_type) vs.slot_type[vs.sp - 1] = SLOT_BOXED; MIR_reg_t rs = vstack_top(&vs); MIR_label_t slow = MIR_new_label(ctx); MIR_label_t done = MIR_new_label(ctx); @@ -2609,6 +3631,30 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } + case OP_POST_INC: { + int top_idx = vs.sp - 1; + vstack_ensure_boxed(&vs, top_idx, ctx, jit_func, r_d_slot); + + MIR_reg_t rold = vstack_top(&vs); + MIR_reg_t rnew = vstack_push(&vs); + + int pin = arith_n++; + char pi_d1[32], pi_d2[32]; + snprintf(pi_d1, sizeof(pi_d1), "pi_d1_%d", pin); + snprintf(pi_d2, sizeof(pi_d2), "pi_d2_%d", pin); + MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, pi_d1); + MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, pi_d2); + + mir_i64_to_d(ctx, jit_func, fd1, rold, r_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DADD, + MIR_new_reg_op(ctx, fd2), + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, r_d_one))); + mir_d_to_i64(ctx, jit_func, rnew, fd2, r_d_slot); + break; + } + case OP_IS_UNDEF: case OP_IS_NULL: { MIR_reg_t rs = vstack_top(&vs); @@ -3033,14 +4079,18 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } + case OP_JMP_FALSE_PEEK: + case OP_JMP_TRUE_PEEK: case OP_JMP_FALSE: case OP_JMP_FALSE8: case OP_JMP_TRUE: case OP_JMP_TRUE8: { vstack_flush_to_boxed(&vs, ctx, jit_func, r_d_slot); - MIR_reg_t cond = vstack_pop(&vs); + bool is_peek = (op == OP_JMP_FALSE_PEEK || op == OP_JMP_TRUE_PEEK); + MIR_reg_t cond = is_peek ? vstack_top(&vs) : vstack_pop(&vs); bool short_op = (op == OP_JMP_FALSE8 || op == OP_JMP_TRUE8); - bool is_false_branch = (op == OP_JMP_FALSE || op == OP_JMP_FALSE8); + bool is_false_branch = (op == OP_JMP_FALSE || op == OP_JMP_FALSE8 + || op == OP_JMP_FALSE_PEEK); int target = bc_off + sz + (short_op ? (int8_t)sv_get_i8(ip + 1) : sv_get_i32(ip + 1)); MIR_label_t lbl = label_for_branch(ctx, &lm, target, vs.sp); @@ -3157,7 +4207,11 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos if (!is_tail) { sv_func_t *inline_callee = vs.known_func[vs.sp - call_argc - 1]; - if (inline_callee && jit_inlineable(inline_callee)) { + if (!inline_callee) + inline_callee = sv_tfb_get_call_target(func, bc_off); + bool speculative = (inline_callee && !vs.known_func[vs.sp - call_argc - 1]); + if (inline_callee && jit_inlineable(inline_callee) + && jit_inline_body_feasible(inline_callee)) { int cn = call_n++; MIR_reg_t inl_arg_regs[call_argc > 0 ? call_argc : 1]; @@ -3170,11 +4224,58 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_label_t inl_slow = MIR_new_label(ctx); MIR_label_t inl_join = MIR_new_label(ctx); + MIR_reg_t r_inl_cl = 0; + { + char cl_rn[32]; snprintf(cl_rn, sizeof(cl_rn), "inl%d_cl", cn); + r_inl_cl = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cl_rn); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, r_inl_cl), + MIR_new_reg_op(ctx, r_inl_callee), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + } + + if (speculative) { + char gt_rn[32]; snprintf(gt_rn, sizeof(gt_rn), "inl%d_gt", cn); + MIR_reg_t r_guard_tag = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, gt_rn); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_guard_tag), + MIR_new_reg_op(ctx, r_inl_callee), + MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, inl_slow), + MIR_new_reg_op(ctx, r_guard_tag), + MIR_new_uint_op(ctx, NANBOX_TFUNC_TAG))); + + char gf_rn[32]; snprintf(gf_rn, sizeof(gf_rn), "inl%d_gf", cn); + MIR_reg_t r_guard_fn = MIR_new_func_reg(ctx, jit_func->u.func, + MIR_T_I64, gf_rn); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_guard_fn), + MIR_new_mem_op(ctx, MIR_T_P, + (MIR_disp_t)offsetof(sv_closure_t, func), + r_inl_cl, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, inl_slow), + MIR_new_reg_op(ctx, r_guard_fn), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)inline_callee))); + } + bool inlined = jit_emit_inline_body( ctx, jit_func, inline_callee, inl_arg_regs, (int)call_argc, r_call_res, inl_slow, inl_join, - r_bool, &r_d_slot, cn); + r_bool, &r_d_slot, cn, + r_inl_cl, r_this, + r_vm, r_js, + helper2_proto, imp_seq, imp_sne, imp_eq, imp_ne, + gf_proto, imp_get_field, + gg_proto, imp_gg); if (inlined) { MIR_append_insn(ctx, jit_func, inl_slow); @@ -3189,7 +4290,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos char rn_sl_this[32]; snprintf(rn_sl_this, sizeof(rn_sl_this), "inl%d_slow_t", cn); MIR_reg_t r_slow_this = MIR_new_func_reg(ctx, jit_func->u.func, - MIR_JSVAL, rn_sl_this); + MIR_JSVAL, rn_sl_this); mir_load_imm(ctx, jit_func, r_slow_this, mkval(T_UNDEF, 0)); MIR_append_insn(ctx, jit_func, @@ -3208,6 +4309,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } + MIR_append_insn(ctx, jit_func, inl_slow); + MIR_append_insn(ctx, jit_func, inl_join); vs.sp = vs.sp - 1 + call_argc + 1; } } @@ -3632,7 +4735,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_reg_t dst = vstack_push(&vs); if (vs.known_func) { - ant_value_t gv = jit_helper_get_global(js, atom->str, atom->len); + ant_value_t gv = jit_helper_get_global(js, atom->str, func, bc_off); if (vtype(gv) == T_FUNC) { sv_closure_t *gcl = js_func_closure(gv); if (gcl && gcl->func == func) { @@ -3651,13 +4754,14 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } MIR_append_insn(ctx, jit_func, - MIR_new_call_insn(ctx, 6, + MIR_new_call_insn(ctx, 7, MIR_new_ref_op(ctx, gg_proto), MIR_new_ref_op(ctx, imp_gg), MIR_new_reg_op(ctx, dst), MIR_new_reg_op(ctx, r_js), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)atom->str), - MIR_new_uint_op(ctx, (uint64_t)atom->len))); + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), + MIR_new_int_op(ctx, (int64_t)bc_off))); break; } @@ -3680,19 +4784,30 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint8_t idx = sv_get_u8(ip + 1); if (idx >= (uint8_t)n_locals) { ok = false; break; } if (known_func_locals) known_func_locals[idx] = NULL; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; + bool loc_is_num = known_type_locals && known_type_locals[idx] == SV_TI_NUM; int in = arith_n++; char il_d1[32], il_d2[32]; snprintf(il_d1, sizeof(il_d1), "il_d1_%d", in); snprintf(il_d2, sizeof(il_d2), "il_d2_%d", in); MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, il_d1); MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, il_d2); - mir_i64_to_d(ctx, jit_func, fd1, local_regs[idx], r_d_slot); + if (loc_is_num && local_d_regs) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, local_d_regs[idx]))); + else + mir_i64_to_d(ctx, jit_func, fd1, local_regs[idx], r_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_DADD, MIR_new_reg_op(ctx, fd2), MIR_new_reg_op(ctx, fd1), MIR_new_reg_op(ctx, r_d_one))); + if (local_d_regs) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, fd2))); mir_d_to_i64(ctx, jit_func, local_regs[idx], fd2, r_d_slot); if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, @@ -3707,19 +4822,30 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint8_t idx = sv_get_u8(ip + 1); if (idx >= (uint8_t)n_locals) { ok = false; break; } if (known_func_locals) known_func_locals[idx] = NULL; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; + bool loc_is_num = known_type_locals && known_type_locals[idx] == SV_TI_NUM; int dn = arith_n++; char dl_d1[32], dl_d2[32]; snprintf(dl_d1, sizeof(dl_d1), "dl_d1_%d", dn); snprintf(dl_d2, sizeof(dl_d2), "dl_d2_%d", dn); MIR_reg_t fd1 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, dl_d1); MIR_reg_t fd2 = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, dl_d2); - mir_i64_to_d(ctx, jit_func, fd1, local_regs[idx], r_d_slot); + if (loc_is_num && local_d_regs) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, fd1), + MIR_new_reg_op(ctx, local_d_regs[idx]))); + else + mir_i64_to_d(ctx, jit_func, fd1, local_regs[idx], r_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_DSUB, MIR_new_reg_op(ctx, fd2), MIR_new_reg_op(ctx, fd1), MIR_new_reg_op(ctx, r_d_one))); + if (local_d_regs) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, fd2))); mir_d_to_i64(ctx, jit_func, local_regs[idx], fd2, r_d_slot); if (has_captures && captured_locals && captured_locals[idx]) MIR_append_insn(ctx, jit_func, @@ -3734,7 +4860,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint8_t idx = sv_get_u8(ip + 1); if (idx >= (uint8_t)n_locals) { ok = false; break; } if (known_func_locals) known_func_locals[idx] = NULL; - if (known_type_locals) known_type_locals[idx] = SV_TI_UNKNOWN; + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t rr = vstack_pop(&vs); MIR_label_t slow = MIR_new_label(ctx); @@ -3758,6 +4884,11 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, fd3), MIR_new_reg_op(ctx, fd1), MIR_new_reg_op(ctx, fd2))); + if (local_d_regs) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_DMOV, + MIR_new_reg_op(ctx, local_d_regs[idx]), + MIR_new_reg_op(ctx, fd3))); mir_d_to_i64(ctx, jit_func, local_regs[idx], fd3, r_d_slot); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, done))); @@ -3786,6 +4917,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } case OP_TO_PROPKEY: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t src = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); MIR_label_t is_key = MIR_new_label(ctx); @@ -3840,8 +4972,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint32_t idx = sv_get_u32(ip + 1); if (idx >= (uint32_t)func->atom_count) { ok = false; break; } sv_atom_t *atom = &func->atoms[idx]; + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t obj = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); + uint16_t ic_idx = sv_get_u16(ip + 5); + MIR_label_t no_err = MIR_new_label(ctx); + MIR_label_t slow = MIR_new_label(ctx); + bool has_fast_path = mir_emit_get_field_ic_fastpath( + ctx, jit_func, func, bc_off, ic_idx, atom, obj, dst, slow, r_ic_epoch_val); + if (has_fast_path) { + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, no_err))); + MIR_append_insn(ctx, jit_func, slow); + } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 10, MIR_new_ref_op(ctx, gf_proto), @@ -3854,7 +4998,6 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_uint_op(ctx, (uint64_t)atom->len), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), MIR_new_int_op(ctx, (int64_t)bc_off))); - MIR_label_t no_err = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), @@ -3886,8 +5029,20 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint32_t idx = sv_get_u32(ip + 1); if (idx >= (uint32_t)func->atom_count) { ok = false; break; } sv_atom_t *atom = &func->atoms[idx]; + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t obj = vstack_top(&vs); MIR_reg_t dst = vstack_push(&vs); + uint16_t ic_idx = sv_get_u16(ip + 5); + MIR_label_t no_err = MIR_new_label(ctx); + MIR_label_t slow = MIR_new_label(ctx); + bool has_fast_path = mir_emit_get_field_ic_fastpath( + ctx, jit_func, func, bc_off, ic_idx, atom, obj, dst, slow, r_ic_epoch_val); + if (has_fast_path) { + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, no_err))); + MIR_append_insn(ctx, jit_func, slow); + } MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 10, MIR_new_ref_op(ctx, gf_proto), @@ -3900,7 +5055,6 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_uint_op(ctx, (uint64_t)atom->len), MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), MIR_new_int_op(ctx, (int64_t)bc_off))); - MIR_label_t no_err = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), @@ -3933,6 +5087,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos if (idx >= (uint32_t)func->atom_count) { ok = false; break; } sv_atom_t *atom = &func->atoms[idx]; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t val = vstack_pop(&vs); MIR_reg_t obj = vstack_pop(&vs); MIR_append_insn(ctx, jit_func, @@ -3979,6 +5134,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos if (idx >= (uint32_t)func->atom_count) { ok = false; break; } sv_atom_t *atom = &func->atoms[idx]; vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t val = vstack_pop(&vs); MIR_reg_t obj = vstack_top(&vs); MIR_append_insn(ctx, jit_func, @@ -3995,6 +5151,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } case OP_GET_ELEM: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t key = vstack_pop(&vs); MIR_reg_t obj = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); @@ -4038,6 +5196,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos } case OP_GET_ELEM2: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t key = vstack_pop(&vs); MIR_reg_t obj = vstack_top(&vs); MIR_reg_t dst = vstack_push(&vs); @@ -4074,6 +5234,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_PUT_ELEM: { vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 3, ctx, jit_func, r_d_slot); MIR_reg_t val = vstack_pop(&vs); MIR_reg_t key = vstack_pop(&vs); MIR_reg_t obj = vstack_pop(&vs); @@ -4257,14 +5419,176 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } - case OP_IN: { - MIR_reg_t rr = vstack_pop(&vs); - MIR_reg_t rl = vstack_pop(&vs); - MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_in, - r_vm, r_js, rl, rr); - MIR_label_t no_err = MIR_new_label(ctx); + case OP_IN: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); + MIR_reg_t rr = vstack_pop(&vs); + MIR_reg_t rl = vstack_pop(&vs); + MIR_reg_t dst = vstack_push(&vs); + mir_call_helper2(ctx, jit_func, dst, + helper2_proto, imp_in, + r_vm, r_js, rl, rr); + MIR_label_t no_err = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, dst), + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, no_err), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, JIT_ERR_TAG))); + if (jit_try_depth > 0) { + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), + MIR_new_reg_op(ctx, dst))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, h->catch_label))); + } else { + MIR_append_insn(ctx, jit_func, + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, dst))); + } + MIR_append_insn(ctx, jit_func, no_err); + break; + } + + case OP_INSTANCEOF: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); + MIR_reg_t rr = vstack_pop(&vs); + MIR_reg_t rl = vstack_pop(&vs); + MIR_reg_t dst = vstack_push(&vs); + uint16_t ic_idx = sv_get_u16(ip + 1); + bool has_ic = func->ic_slots && ic_idx < func->ic_count; + MIR_label_t slow = has_ic ? MIR_new_label(ctx) : NULL; + MIR_label_t no_err = MIR_new_label(ctx); + + if (has_ic) { + sv_ic_entry_t *ic = &func->ic_slots[ic_idx]; + char inst_ic_name[32], inst_ice_name[32]; + char inst_rt_name[32], inst_ro_name[32], inst_ica_name[32], inst_lt_name[32]; + char inst_lo_name[32], inst_ls_name[32], inst_ics_name[32], inst_ici_name[32]; + snprintf(inst_ic_name, sizeof(inst_ic_name), "inst_ic_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ice_name, sizeof(inst_ice_name), "inst_ice_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_rt_name, sizeof(inst_rt_name), "inst_rt_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ro_name, sizeof(inst_ro_name), "inst_ro_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ica_name, sizeof(inst_ica_name), "inst_ica_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_lt_name, sizeof(inst_lt_name), "inst_lt_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_lo_name, sizeof(inst_lo_name), "inst_lo_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ls_name, sizeof(inst_ls_name), "inst_ls_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ics_name, sizeof(inst_ics_name), "inst_ics_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(inst_ici_name, sizeof(inst_ici_name), "inst_ici_%d_%u", bc_off, (unsigned)ic_idx); + + MIR_reg_t r_ic = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ic_name); + MIR_reg_t r_ic_epoch = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ice_name); + MIR_reg_t r_rhs_tag = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_rt_name); + MIR_reg_t r_rhs_obj = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ro_name); + MIR_reg_t r_ic_aux = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ica_name); + MIR_reg_t r_lhs_tag = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_lt_name); + MIR_reg_t r_lhs_obj = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_lo_name); + MIR_reg_t r_lhs_shape = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ls_name); + MIR_reg_t r_ic_shape = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ics_name); + MIR_reg_t r_ic_idx_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, inst_ici_name); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)ic))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_mem_op(ctx, MIR_T_I32, + (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_ic_epoch_val))); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_rhs_tag), + MIR_new_reg_op(ctx, rr), + MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_rhs_tag), + MIR_new_uint_op(ctx, NANBOX_TFUNC_TAG))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, r_rhs_obj), + MIR_new_reg_op(ctx, rr), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_aux), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_aux), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_rhs_obj), + MIR_new_reg_op(ctx, r_ic_aux))); + + mir_emit_value_to_objptr_or_jmp( + ctx, jit_func, rl, r_lhs_obj, r_lhs_tag, slow); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_lhs_shape), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(ant_object_t, shape), r_lhs_obj, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_shape), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_shape), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_lhs_shape), + MIR_new_reg_op(ctx, r_ic_shape))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_mem_op(ctx, MIR_T_I32, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_index), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_OR, + MIR_new_reg_op(ctx, dst), + MIR_new_uint_op(ctx, js_false), + MIR_new_reg_op(ctx, r_ic_idx_val))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, no_err))); + + MIR_append_insn(ctx, jit_func, slow); + } + + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 9, + MIR_new_ref_op(ctx, inst_proto), + MIR_new_ref_op(ctx, imp_instanceof), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, rl), + MIR_new_reg_op(ctx, rr), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), + MIR_new_int_op(ctx, (int64_t)bc_off))); + if (has_captures) { + for (int i = 0; i < n_locals; i++) + if (captured_locals[i]) + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, local_regs[i]), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); + } MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), @@ -4292,13 +5616,125 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } - case OP_INSTANCEOF: { - MIR_reg_t rr = vstack_pop(&vs); - MIR_reg_t rl = vstack_pop(&vs); + case OP_CALL_IS_PROTO: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 3, ctx, jit_func, r_d_slot); + MIR_reg_t arg = vstack_pop(&vs); + MIR_reg_t fn = vstack_pop(&vs); + MIR_reg_t this_obj = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_instanceof, - r_vm, r_js, rl, rr); + uint16_t ic_idx = sv_get_u16(ip + 1); + bool has_ic = func->ic_slots && ic_idx < func->ic_count; + MIR_label_t slow = has_ic ? MIR_new_label(ctx) : NULL; + MIR_label_t no_err = MIR_new_label(ctx); + + if (has_ic) { + sv_ic_entry_t *ic = &func->ic_slots[ic_idx]; + char cip_bi_name[32], cip_ic_name[32], cip_ice_name[32]; + char cip_pt_name[32], cip_pp_name[32], cip_ot_name[32]; + char cip_op_name[32], cip_ich_name[32], cip_ics_name[32], cip_ici_name[32]; + snprintf(cip_bi_name, sizeof(cip_bi_name), "cip_bi_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ic_name, sizeof(cip_ic_name), "cip_ic_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ice_name, sizeof(cip_ice_name), "cip_ice_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_pt_name, sizeof(cip_pt_name), "cip_pt_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_pp_name, sizeof(cip_pp_name), "cip_pp_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ot_name, sizeof(cip_ot_name), "cip_ot_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_op_name, sizeof(cip_op_name), "cip_op_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ich_name, sizeof(cip_ich_name), "cip_ich_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ics_name, sizeof(cip_ics_name), "cip_ics_%d_%u", bc_off, (unsigned)ic_idx); + snprintf(cip_ici_name, sizeof(cip_ici_name), "cip_ici_%d_%u", bc_off, (unsigned)ic_idx); + + MIR_reg_t r_builtin = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_bi_name); + MIR_reg_t r_ic = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ic_name); + MIR_reg_t r_ic_epoch = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ice_name); + MIR_reg_t r_proto_tag = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_pt_name); + MIR_reg_t r_proto_ptr = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_pp_name); + MIR_reg_t r_obj_tag = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ot_name); + MIR_reg_t r_obj_ptr = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_op_name); + MIR_reg_t r_ic_holder = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ich_name); + MIR_reg_t r_ic_shape = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ics_name); + MIR_reg_t r_ic_idx_val = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, cip_ici_name); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_builtin), + MIR_new_uint_op(ctx, (uint64_t)js_mkfun(builtin_object_isPrototypeOf)))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, fn), + MIR_new_reg_op(ctx, r_builtin))); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)ic))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_mem_op(ctx, MIR_T_I32, + (MIR_disp_t)offsetof(sv_ic_entry_t, epoch), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_ic_epoch), + MIR_new_reg_op(ctx, r_ic_epoch_val))); + + mir_emit_value_to_objptr_or_jmp( + ctx, jit_func, this_obj, r_proto_ptr, r_proto_tag, slow); + mir_emit_value_to_objptr_or_jmp( + ctx, jit_func, arg, r_obj_ptr, r_obj_tag, slow); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_holder), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_holder), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_proto_ptr), + MIR_new_reg_op(ctx, r_ic_holder))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_shape), + MIR_new_mem_op(ctx, MIR_T_I64, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_shape), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, slow), + MIR_new_reg_op(ctx, r_obj_ptr), + MIR_new_reg_op(ctx, r_ic_shape))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, r_ic_idx_val), + MIR_new_mem_op(ctx, MIR_T_I32, + (MIR_disp_t)offsetof(sv_ic_entry_t, cached_index), r_ic, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_OR, + MIR_new_reg_op(ctx, dst), + MIR_new_uint_op(ctx, js_false), + MIR_new_reg_op(ctx, r_ic_idx_val))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, no_err))); + + MIR_append_insn(ctx, jit_func, slow); + } + + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 10, + MIR_new_ref_op(ctx, call_is_proto), + MIR_new_ref_op(ctx, imp_call_is_proto), + MIR_new_reg_op(ctx, dst), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, this_obj), + MIR_new_reg_op(ctx, fn), + MIR_new_reg_op(ctx, arg), + MIR_new_uint_op(ctx, (uint64_t)(uintptr_t)func), + MIR_new_int_op(ctx, (int64_t)bc_off))); if (has_captures) { for (int i = 0; i < n_locals; i++) if (captured_locals[i]) @@ -4308,7 +5744,6 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_mem_op(ctx, MIR_T_I64, (MIR_disp_t)(i * (int)sizeof(ant_value_t)), r_lbuf, 0, 1))); } - MIR_label_t no_err = MIR_new_label(ctx); MIR_append_insn(ctx, jit_func, MIR_new_insn(ctx, MIR_URSH, MIR_new_reg_op(ctx, r_bool), @@ -4339,6 +5774,50 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_GET_LENGTH: { MIR_reg_t obj = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); + + char gl_tag_name[32], gl_ptr_name[32], gl_len_name[32], gl_dbl_name[32]; + snprintf(gl_tag_name, sizeof(gl_tag_name), "gl_tag_%d", bc_off); + snprintf(gl_ptr_name, sizeof(gl_ptr_name), "gl_ptr_%d", bc_off); + snprintf(gl_len_name, sizeof(gl_len_name), "gl_len_%d", bc_off); + snprintf(gl_dbl_name, sizeof(gl_dbl_name), "gl_dbl_%d", bc_off); + MIR_reg_t gl_tag = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, gl_tag_name); + MIR_reg_t gl_ptr = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, gl_ptr_name); + MIR_reg_t gl_len = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_I64, gl_len_name); + MIR_reg_t gl_dbl = MIR_new_func_reg(ctx, jit_func->u.func, MIR_T_D, gl_dbl_name); + MIR_label_t gl_slow = MIR_new_label(ctx); + MIR_label_t gl_done = MIR_new_label(ctx); + + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, gl_tag), + MIR_new_reg_op(ctx, obj), + MIR_new_uint_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, gl_slow), + MIR_new_reg_op(ctx, gl_tag), + MIR_new_uint_op(ctx, NANBOX_TARR_TAG))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_AND, + MIR_new_reg_op(ctx, gl_ptr), + MIR_new_reg_op(ctx, obj), + MIR_new_uint_op(ctx, NANBOX_DATA_MASK))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, gl_len), + MIR_new_mem_op(ctx, MIR_T_U32, + (MIR_disp_t)offsetof(ant_object_t, u.array.len), + gl_ptr, 0, 1))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_UI2D, + MIR_new_reg_op(ctx, gl_dbl), + MIR_new_reg_op(ctx, gl_len))); + mir_d_to_i64(ctx, jit_func, dst, gl_dbl, r_d_slot); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, gl_done))); + + MIR_append_insn(ctx, jit_func, gl_slow); MIR_append_insn(ctx, jit_func, MIR_new_call_insn(ctx, 6, MIR_new_ref_op(ctx, helper1_proto), @@ -4347,46 +5826,153 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_reg_op(ctx, r_vm), MIR_new_reg_op(ctx, r_js), MIR_new_reg_op(ctx, obj))); + MIR_append_insn(ctx, jit_func, gl_done); break; } case OP_SEQ: { + bool r_const = vs.has_const && vs.has_const[vs.sp - 1]; + bool l_const = vs.has_const && vs.has_const[vs.sp - 2]; + uint64_t cval = r_const ? vs.known_const[vs.sp - 1] + : l_const ? vs.known_const[vs.sp - 2] : 0; + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t rr = vstack_pop(&vs); MIR_reg_t rl = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_seq, - r_vm, r_js, rl, rr); + if (r_const || l_const) { + MIR_reg_t other = r_const ? rl : rr; + MIR_label_t is_true = MIR_new_label(ctx); + MIR_label_t is_done = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_true), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, cval))); + mir_load_imm(ctx, jit_func, dst, js_false); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); + MIR_append_insn(ctx, jit_func, is_true); + mir_load_imm(ctx, jit_func, dst, js_true); + MIR_append_insn(ctx, jit_func, is_done); + } else { + mir_call_helper2(ctx, jit_func, dst, + helper2_proto, imp_seq, + r_vm, r_js, rl, rr); + } break; } case OP_EQ: { + bool r_const = vs.has_const && vs.has_const[vs.sp - 1]; + bool l_const = vs.has_const && vs.has_const[vs.sp - 2]; + uint64_t cval = r_const ? vs.known_const[vs.sp - 1] + : l_const ? vs.known_const[vs.sp - 2] : 0; + bool is_nullish = (r_const || l_const) && + (cval == mkval(T_NULL, 0) || cval == mkval(T_UNDEF, 0)); + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t rr = vstack_pop(&vs); MIR_reg_t rl = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_eq, - r_vm, r_js, rl, rr); + if (is_nullish) { + MIR_reg_t other = r_const ? rl : rr; + MIR_label_t is_true = MIR_new_label(ctx); + MIR_label_t is_done = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_true), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, mkval(T_NULL, 0)))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_true), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, mkval(T_UNDEF, 0)))); + mir_load_imm(ctx, jit_func, dst, js_false); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); + MIR_append_insn(ctx, jit_func, is_true); + mir_load_imm(ctx, jit_func, dst, js_true); + MIR_append_insn(ctx, jit_func, is_done); + } else { + mir_call_helper2(ctx, jit_func, dst, + helper2_proto, imp_eq, + r_vm, r_js, rl, rr); + } break; } case OP_NE: { + bool r_const = vs.has_const && vs.has_const[vs.sp - 1]; + bool l_const = vs.has_const && vs.has_const[vs.sp - 2]; + uint64_t cval = r_const ? vs.known_const[vs.sp - 1] + : l_const ? vs.known_const[vs.sp - 2] : 0; + bool is_nullish = (r_const || l_const) && + (cval == mkval(T_NULL, 0) || cval == mkval(T_UNDEF, 0)); + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t rr = vstack_pop(&vs); MIR_reg_t rl = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_ne, - r_vm, r_js, rl, rr); + if (is_nullish) { + MIR_reg_t other = r_const ? rl : rr; + MIR_label_t is_false = MIR_new_label(ctx); + MIR_label_t is_done = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_false), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, mkval(T_NULL, 0)))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_false), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, mkval(T_UNDEF, 0)))); + mir_load_imm(ctx, jit_func, dst, js_true); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); + MIR_append_insn(ctx, jit_func, is_false); + mir_load_imm(ctx, jit_func, dst, js_false); + MIR_append_insn(ctx, jit_func, is_done); + } else { + mir_call_helper2(ctx, jit_func, dst, + helper2_proto, imp_ne, + r_vm, r_js, rl, rr); + } break; } case OP_SNE: { + bool r_const = vs.has_const && vs.has_const[vs.sp - 1]; + bool l_const = vs.has_const && vs.has_const[vs.sp - 2]; + uint64_t cval = r_const ? vs.known_const[vs.sp - 1] + : l_const ? vs.known_const[vs.sp - 2] : 0; + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t rr = vstack_pop(&vs); MIR_reg_t rl = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); - mir_call_helper2(ctx, jit_func, dst, - helper2_proto, imp_sne, - r_vm, r_js, rl, rr); + if (r_const || l_const) { + MIR_reg_t other = r_const ? rl : rr; + MIR_label_t is_false = MIR_new_label(ctx); + MIR_label_t is_done = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BEQ, + MIR_new_label_op(ctx, is_false), + MIR_new_reg_op(ctx, other), + MIR_new_uint_op(ctx, cval))); + mir_load_imm(ctx, jit_func, dst, js_true); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, MIR_new_label_op(ctx, is_done))); + MIR_append_insn(ctx, jit_func, is_false); + mir_load_imm(ctx, jit_func, dst, js_false); + MIR_append_insn(ctx, jit_func, is_done); + } else { + mir_call_helper2(ctx, jit_func, dst, + helper2_proto, imp_sne, + r_vm, r_js, rl, rr); + } break; } @@ -4793,10 +6379,16 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos case OP_VOID: { MIR_reg_t rs = vstack_top(&vs); mir_load_imm(ctx, jit_func, rs, mkval(T_UNDEF, 0)); + if (vs.has_const) { + vs.has_const[vs.sp - 1] = true; + vs.known_const[vs.sp - 1] = mkval(T_UNDEF, 0); + } break; } case OP_DELETE: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); + vstack_ensure_boxed(&vs, vs.sp - 2, ctx, jit_func, r_d_slot); MIR_reg_t rk = vstack_pop(&vs); MIR_reg_t ro = vstack_pop(&vs); MIR_reg_t dst = vstack_push(&vs); @@ -4835,6 +6427,8 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos uint16_t new_argc = sv_get_u16(ip + 1); if (new_argc > 16 || vs.sp < (int)new_argc + 2) { ok = false; break; } + for (int i = 0; i < (int)new_argc + 2; i++) + vstack_ensure_boxed(&vs, vs.sp - 1 - i, ctx, jit_func, r_d_slot); for (int i = (int)new_argc - 1; i >= 0; i--) { MIR_reg_t areg = vstack_pop(&vs); MIR_append_insn(ctx, jit_func, @@ -5069,6 +6663,81 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; } + case OP_APPLY: { + vstack_flush_to_boxed(&vs, ctx, jit_func, r_d_slot); + uint16_t apply_argc = sv_get_u16(ip + 1); + if (apply_argc > 16 || vs.sp < (int)apply_argc + 2) { ok = false; break; } + + int cn = call_n++; + MIR_reg_t r_arg_arr = r_args_buf; + + for (int i = (int)apply_argc - 1; i >= 0; i--) { + MIR_reg_t areg = vstack_pop(&vs); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_mem_op(ctx, MIR_JSVAL, + (MIR_disp_t)(i * (int)sizeof(ant_value_t)), + r_arg_arr, 0, 1), + MIR_new_reg_op(ctx, areg))); + } + + MIR_reg_t r_apply_this = vstack_pop(&vs); + MIR_reg_t r_apply_func = vstack_pop(&vs); + MIR_reg_t r_apply_res = vstack_push(&vs); + + MIR_append_insn(ctx, jit_func, + MIR_new_call_insn(ctx, 9, + MIR_new_ref_op(ctx, call_proto), + MIR_new_ref_op(ctx, imp_apply), + MIR_new_reg_op(ctx, r_apply_res), + MIR_new_reg_op(ctx, r_vm), + MIR_new_reg_op(ctx, r_js), + MIR_new_reg_op(ctx, r_apply_func), + MIR_new_reg_op(ctx, r_apply_this), + MIR_new_reg_op(ctx, r_arg_arr), + MIR_new_int_op(ctx, (int64_t)apply_argc))); + + if (jit_try_depth > 0) { + jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; + MIR_label_t no_err = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, r_apply_res), + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, no_err), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, JIT_ERR_TAG))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_MOV, + MIR_new_reg_op(ctx, vs.regs[h->saved_sp]), + MIR_new_reg_op(ctx, r_apply_res))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_JMP, + MIR_new_label_op(ctx, h->catch_label))); + MIR_append_insn(ctx, jit_func, no_err); + } else { + MIR_label_t no_err = MIR_new_label(ctx); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_URSH, + MIR_new_reg_op(ctx, r_bool), + MIR_new_reg_op(ctx, r_apply_res), + MIR_new_int_op(ctx, NANBOX_TYPE_SHIFT))); + MIR_append_insn(ctx, jit_func, + MIR_new_insn(ctx, MIR_BNE, + MIR_new_label_op(ctx, no_err), + MIR_new_reg_op(ctx, r_bool), + MIR_new_uint_op(ctx, JIT_ERR_TAG))); + MIR_append_insn(ctx, jit_func, + MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_apply_res))); + MIR_append_insn(ctx, jit_func, no_err); + } + (void)cn; + break; + } + case OP_NOP: case OP_HALT: case OP_LINE_NUM: @@ -5116,6 +6785,7 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos break; case OP_THROW: { + vstack_ensure_boxed(&vs, vs.sp - 1, ctx, jit_func, r_d_slot); MIR_reg_t thrown = vstack_pop(&vs); if (jit_try_depth > 0) { jit_try_entry_t *h = &jit_try_stack[jit_try_depth - 1]; @@ -5277,14 +6947,18 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos MIR_new_ret_insn(ctx, 1, MIR_new_reg_op(ctx, r_resume_res))); } - MIR_finish_func(ctx); MIR_finish_module(ctx); + MIR_finish_func(ctx); + MIR_finish_module(ctx); if (sv_dump_jit_unlikely) MIR_output_module(ctx, stderr, mod); free(vs.regs); free(vs.d_regs); free(vs.known_func); free(vs.slot_type); + free(vs.known_const); + free(vs.has_const); free(local_regs); + free(local_d_regs); free(known_func_locals); free(known_type_locals); free(captured_locals); @@ -5292,65 +6966,26 @@ sv_jit_func_t sv_jit_compile(ant_t *js, sv_func_t *func, sv_closure_t *hint_clos if (!ok) return NULL; MIR_load_module(ctx, mod); -#define LOAD_EXT(name) MIR_load_external(ctx, #name, name) - LOAD_EXT(jit_helper_add); - LOAD_EXT(jit_helper_sub); - LOAD_EXT(jit_helper_mul); - LOAD_EXT(jit_helper_div); - LOAD_EXT(jit_helper_mod); - LOAD_EXT(jit_helper_lt); - LOAD_EXT(jit_helper_le); - LOAD_EXT(jit_helper_gt); - LOAD_EXT(jit_helper_ge); - LOAD_EXT(jit_helper_call); - LOAD_EXT(jit_helper_tov); - LOAD_EXT(jit_helper_get_global); - LOAD_EXT(jit_helper_get_field); - LOAD_EXT(jit_helper_to_propkey); - LOAD_EXT(jit_helper_bailout_resume); - LOAD_EXT(jit_helper_close_upval); - LOAD_EXT(jit_helper_closure); - LOAD_EXT(jit_helper_in); - LOAD_EXT(jit_helper_instanceof); - LOAD_EXT(jit_helper_get_length); - LOAD_EXT(jit_helper_define_field); - LOAD_EXT(jit_helper_seq); - LOAD_EXT(jit_helper_eq); - LOAD_EXT(jit_helper_ne); - LOAD_EXT(jit_helper_sne); - LOAD_EXT(jit_helper_put_field); - LOAD_EXT(jit_helper_get_elem); - LOAD_EXT(jit_helper_get_elem2); - LOAD_EXT(jit_helper_put_elem); - LOAD_EXT(jit_helper_put_global); - LOAD_EXT(jit_helper_object); - LOAD_EXT(jit_helper_array); - LOAD_EXT(jit_helper_catch_value); - LOAD_EXT(jit_helper_throw); - LOAD_EXT(jit_helper_throw_error); - LOAD_EXT(jit_helper_set_proto); - LOAD_EXT(jit_helper_band); - LOAD_EXT(jit_helper_bor); - LOAD_EXT(jit_helper_bxor); - LOAD_EXT(jit_helper_bnot); - LOAD_EXT(jit_helper_shl); - LOAD_EXT(jit_helper_shr); - LOAD_EXT(jit_helper_ushr); - LOAD_EXT(jit_helper_not); - LOAD_EXT(jit_helper_is_truthy); - LOAD_EXT(jit_helper_typeof); - LOAD_EXT(jit_helper_new); - LOAD_EXT(jit_helper_delete); - LOAD_EXT(jit_helper_set_name); - LOAD_EXT(jit_helper_stack_overflow); - LOAD_EXT(jit_helper_stack_overflow_error); -#undef LOAD_EXT MIR_link(ctx, MIR_set_gen_interface, NULL); func->jit_compiled_tfb_ver = func->tfb_version; + func->jit_compiling = false; return MIR_gen(ctx, jit_func); } +static void sv_jit_compile_callees(ant_t *js, sv_func_t *func) { + sv_call_target_fb_t *fb = func->call_target_fb; + int count = func->call_target_fb_count; + for (int i = 0; i < count; i++) { + if (fb[i].disabled || !fb[i].target) continue; + sv_func_t *callee = fb[i].target; + if (callee->jit_code || callee->jit_compile_failed || callee->jit_compiling) continue; + if (callee->call_count < SV_JIT_THRESHOLD / 2) continue; + if (!jit_is_eligible(callee)) continue; + sv_jit_func_t cjit = sv_jit_compile(js, callee, NULL); + if (cjit) callee->jit_code = (void *)cjit; + } +} ant_value_t sv_jit_try_compile_and_call( sv_vm_t *vm, ant_t *js, @@ -5367,6 +7002,7 @@ ant_value_t sv_jit_try_compile_and_call( } fn->jit_code = (void *)jit; + sv_jit_compile_callees(js, fn); sv_jit_enter(js); ant_value_t result = jit(vm, ctx->this_val, ctx->args, ctx->argc, closure); sv_jit_leave(js); @@ -5400,9 +7036,15 @@ ant_value_t sv_jit_try_osr( if (func->jit_code) { jit = (sv_jit_func_t)func->jit_code; } else { + if (func->code_len > 512) { + func->call_count = SV_JIT_THRESHOLD + 1; + func->back_edge_count = 0; + return SV_JIT_RETRY_INTERP; + } jit = sv_jit_compile(js, func, closure); if (!jit) return SV_JIT_RETRY_INTERP; func->jit_code = (void *)jit; + sv_jit_compile_callees(js, func); } int nl = func->max_locals; diff --git a/src/snapshot.c b/src/snapshot.c index 9a3419c..feb3200 100644 --- a/src/snapshot.c +++ b/src/snapshot.c @@ -4,6 +4,7 @@ #include "snapshot.h" #include "internal.h" #include "snapshot_data.h" +#include "gc/objects.h" ant_value_t ant_load_snapshot(ant_t *js) { if (!js) return js_mkundef(); @@ -12,6 +13,7 @@ ant_value_t ant_load_snapshot(ant_t *js) { size_t len = ant_snapshot_source_len; ant_value_t result = js_eval_bytecode(js, src, len); + gc_pin_existing_objects(js); return vtype(result) == T_ERR ? result : js_true; } diff --git a/src/sugar.c b/src/sugar.c index b0e07e9..1a9e944 100644 --- a/src/sugar.c +++ b/src/sugar.c @@ -104,7 +104,7 @@ static void resume_coroutine_if_suspended(ant_t *js, coroutine_t *coro) { ant_value_t resume_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - ant_value_t coro_val = js_get_slot(js, me, SLOT_CORO); + ant_value_t coro_val = js_get_slot(me, SLOT_CORO); if (vtype(coro_val) != T_NUM) return js_mkundef(); coroutine_t *coro = (coroutine_t *)(uintptr_t)tod(coro_val); @@ -118,7 +118,7 @@ ant_value_t resume_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs) { ant_value_t reject_coroutine_wrapper(ant_t *js, ant_value_t *args, int nargs) { ant_value_t me = js->current_func; - ant_value_t coro_val = js_get_slot(js, me, SLOT_CORO); + ant_value_t coro_val = js_get_slot(me, SLOT_CORO); if (vtype(coro_val) != T_NUM) return js_mkundef(); diff --git a/src/types/ant.d.ts b/src/types/ant.d.ts index 65cce1f..85622d0 100644 --- a/src/types/ant.d.ts +++ b/src/types/ant.d.ts @@ -13,7 +13,6 @@ type AntType = | 'closure' | 'promise' | 'generator' - | 'prop' | 'err' | 'typedarray' | 'ffi' @@ -34,35 +33,46 @@ type AntHost = | 'sunos' | 'os/2'; -interface AntGcResult { - heapBefore: number; - heapAfter: number; - usedBefore: number; - usedAfter: number; - freed: number; - arenaBefore: number; - arenaAfter: number; - arenaFreed: number; +interface AntPoolInfo { + used: number; + capacity: number; + blocks: number; } -interface AntAllocResult { - arenaSize: number; - heapSize: number; - freeBytes: number; - usedBytes: number; - totalBytes: number; +interface AntPoolStats { + rope: AntPoolInfo; + symbol: AntPoolInfo; + bigint: AntPoolInfo; + string: AntPoolInfo; + totalUsed: number; + totalCapacity: number; } interface AntExternalMemory { buffers: number; code: number; - collections: number; + total: number; +} + +interface AntAllocStats { + objectCount: number; + objects: number; + overflow: number; + extraSlots: number; + promises: number; + proxies: number; + exotic: number; + arrays: number; + shapes: number; + closures: number; + upvalues: number; + propRefs: number; total: number; } interface AntStatsResult { - arenaUsed: number; - arenaSize: number; + pools: AntPoolStats; + alloc: AntAllocStats; external: AntExternalMemory; cstack: number; rss?: number; @@ -70,7 +80,33 @@ interface AntStatsResult { } interface AntRaw { + readonly stack: string; typeof(t: unknown): number; + ctorPropFeedback(fn: Function): AntCtorPropFeedback; + gcMarkProfile(): AntGcMarkProfile; + gcMarkProfileEnable(enabled?: boolean): boolean; + gcMarkProfileReset(): void; +} + +interface AntCtorPropFeedback { + samples: number; + overflowFrom: number; + inobjLimit: number; + inobjLimitFrozen: boolean; + slackRemaining: number; + bins: number[]; + name?: string; + filename?: string; +} + +interface AntGcMarkProfile { + enabled: boolean; + collections: number; + funcVisits: number; + childEdges: number; + constSlots: number; + timeNs: number; + timeMs: number; } interface HttpContext { @@ -103,10 +139,8 @@ interface AntStatic { inspect(...args: unknown[]): void; typeof(t: unknown): AntType | '??'; - raw: AntRaw; - gc(): AntGcResult; - alloc(): AntAllocResult; + raw: AntRaw; stats(): AntStatsResult; sleep(seconds: number): void; diff --git a/src/types/timers.d.ts b/src/types/timers.d.ts index b6e33e9..abb242a 100644 --- a/src/types/timers.d.ts +++ b/src/types/timers.d.ts @@ -1,5 +1,5 @@ -declare function setTimeout(callback: (...args: T) => void, delay: number, ...args: T): number; -declare function setInterval(callback: (...args: T) => void, delay: number, ...args: T): number; +declare function setTimeout(callback: (...args: T) => void, delay?: number, ...args: T): number; +declare function setInterval(callback: (...args: T) => void, delay?: number, ...args: T): number; declare function clearTimeout(timerId: number): void; declare function clearInterval(timerId: number): void; diff --git a/tests/bench_const_eq.js b/tests/bench_const_eq.js new file mode 100644 index 0000000..3991ca2 --- /dev/null +++ b/tests/bench_const_eq.js @@ -0,0 +1,108 @@ +// Benchmark: constant eq/seq inline — measures overhead of calling +// jit_helper_eq/jit_helper_seq when one operand is a compile-time +// constant (true, false, undefined, null) vs. an inline beq/bne. + +function bench(name, fn, iterations) { + const start = Date.now(); + fn(iterations); + const elapsed = Date.now() - start; + console.log(name + ": " + elapsed + "ms (" + iterations + " iters, " + (elapsed / iterations * 1000).toFixed(2) + "µs/op)"); +} + +// 1. === true (most common pattern: result === true) +bench("=== true", function(n) { + let count = 0; + let v = true; + for (let i = 0; i < n; i++) + if (v === true) count++; + return count; +}, 10000000); + +// 2. === false +bench("=== false", function(n) { + let count = 0; + let v = false; + for (let i = 0; i < n; i++) + if (v === false) count++; + return count; +}, 10000000); + +// 3. === undefined +bench("=== undefined", function(n) { + let count = 0; + let v = undefined; + for (let i = 0; i < n; i++) + if (v === undefined) count++; + return count; +}, 10000000); + +// 4. === null +bench("=== null", function(n) { + let count = 0; + let v = null; + for (let i = 0; i < n; i++) + if (v === null) count++; + return count; +}, 10000000); + +// 5. == null (coercing — catches both null and undefined) +bench("== null", function(n) { + let count = 0; + let v = undefined; + for (let i = 0; i < n; i++) + if (v == null) count++; + return count; +}, 10000000); + +// 6. Branching on === true/false (switch-like pattern) +bench("branch true/false/other", function(n) { + let sum = 0; + let vals = [true, false, 42]; + for (let i = 0; i < n; i++) { + let v = vals[i % 3]; + if (v === true) sum += 1; + else if (v === false) sum += 2; + else sum += 3; + } + return sum; +}, 5000000); + +// 7. Optional field check (obj.x === undefined) +bench("field === undefined", function(n) { + let o = { a: 1 }; + let count = 0; + for (let i = 0; i < n; i++) + if (o.b === undefined) count++; + return count; +}, 5000000); + +// 8. Truthiness after === (the truthy-dance pattern from newt) +bench("(x === y) === true", function(n) { + let count = 0; + let a = 5, b = 5; + for (let i = 0; i < n; i++) + if ((a === b) === true) count++; + return count; +}, 10000000); + +// 9. !== undefined guard +bench("!== undefined", function(n) { + let count = 0; + let v = 42; + for (let i = 0; i < n; i++) + if (v !== undefined) count++; + return count; +}, 10000000); + +// 10. Mixed constant comparisons in one function body +bench("mixed const cmp", function(n) { + let sum = 0; + let t = true, f = false, u = undefined, z = null; + for (let i = 0; i < n; i++) { + if (t === true) sum++; + if (f === false) sum++; + if (u === undefined) sum++; + if (z === null) sum++; + } + return sum; +}, 5000000); diff --git a/tests/bench_delete.js b/tests/bench_delete.js new file mode 100644 index 0000000..9e1fb13 --- /dev/null +++ b/tests/bench_delete.js @@ -0,0 +1,78 @@ +// Benchmark: delete operator performance + +function bench(name, fn, iterations) { + const start = Date.now(); + fn(iterations); + const elapsed = Date.now() - start; + console.log(name + ": " + elapsed + "ms (" + iterations + " iters, " + (elapsed / iterations * 1000).toFixed(2) + "µs/op)"); +} + +bench("delete single prop", function(n) { + for (let i = 0; i < n; i++) { + let o = { a: 1 }; + delete o.a; + } +}, 5000); + +bench("delete from 10-prop obj", function(n) { + for (let i = 0; i < n; i++) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }; + delete o.e; + } +}, 5000); + +bench("repeated delete (5 of 10)", function(n) { + for (let i = 0; i < n; i++) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }; + delete o.a; + delete o.b; + delete o.c; + delete o.d; + delete o.e; + } +}, 2000); + +bench("delete + re-add cycle", function(n) { + let o = { x: 1 }; + for (let i = 0; i < n; i++) { + delete o.x; + o.x = i; + } +}, 5000); + +bench("delete non-existent", function(n) { + let o = { a: 1 }; + for (let i = 0; i < n; i++) { + delete o.z; + } +}, 5000); + +bench("delete computed key", function(n) { + for (let i = 0; i < n; i++) { + let o = { a: 1, b: 2, c: 3 }; + delete o["b"]; + } +}, 5000); + +bench("delete from 50-prop obj", function(n) { + for (let i = 0; i < n; i++) { + let o = {}; + for (let j = 0; j < 50; j++) o["k" + j] = j; + delete o["k25"]; + } +}, 1000); + +bench("delete 25 from 50-prop obj", function(n) { + for (let i = 0; i < n; i++) { + let o = {}; + for (let j = 0; j < 50; j++) o["k" + j] = j; + for (let j = 0; j < 25; j++) delete o["k" + j]; + } +}, 500); + +bench("baseline: prop set only", function(n) { + for (let i = 0; i < n; i++) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5 }; + o.f = 6; + } +}, 5000); diff --git a/tests/bench_epoch.js b/tests/bench_epoch.js new file mode 100644 index 0000000..8d3e5b3 --- /dev/null +++ b/tests/bench_epoch.js @@ -0,0 +1,74 @@ +// Benchmark: IC epoch hoist — measures benefit of loading the global +// IC epoch once per function instead of once per GET_FIELD site. + +function bench(name, fn, iterations) { + const start = Date.now(); + fn(iterations); + const elapsed = Date.now() - start; + console.log(name + ": " + elapsed + "ms (" + iterations + " iters, " + (elapsed / iterations * 1000).toFixed(2) + "µs/op)"); +} + +// 1. Many IC sites on the same object (wide read) +bench("10-field single obj", function(n) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }; + let sum = 0; + for (let i = 0; i < n; i++) + sum += o.a + o.b + o.c + o.d + o.e + o.f + o.g + o.h + o.i + o.j; + return sum; +}, 2000000); + +// 2. Chained field access — each dot is a separate IC site +bench("chained field 4-deep", function(n) { + let o = { x: { y: { z: { w: 42 } } } }; + let sum = 0; + for (let i = 0; i < n; i++) sum += o.x.y.z.w; + return sum; +}, 5000000); + +// 3. Multiple objects, multiple fields per iteration +bench("3-obj x 3-field", function(n) { + let a = { x: 1, y: 2, z: 3 }; + let b = { x: 4, y: 5, z: 6 }; + let c = { x: 7, y: 8, z: 9 }; + let sum = 0; + for (let i = 0; i < n; i++) + sum += a.x + a.y + a.z + b.x + b.y + b.z + c.x + c.y + c.z; + return sum; +}, 2000000); + +// 4. Pattern matching style — branch with many IC reads +bench("branchy multi-field", function(n) { + let o = { type: 1, value: 10, name: 20, data: 30, extra: 40 }; + let sum = 0; + for (let i = 0; i < n; i++) { + if (o.type === 1) + sum += o.value + o.name; + else + sum += o.data + o.extra; + } + return sum; +}, 5000000); + +// 5. Prototype chain with many field reads (IC + proto walk) +bench("proto 6-field", function(n) { + function Point(x, y, z) { this.x = x; this.y = y; this.z = z; } + Point.prototype.r = 10; + Point.prototype.g = 20; + Point.prototype.b = 30; + let p = new Point(1, 2, 3); + let sum = 0; + for (let i = 0; i < n; i++) + sum += p.x + p.y + p.z + p.r + p.g + p.b; + return sum; +}, 2000000); + +// 6. Non-loop straight-line many IC sites (function call overhead) +function readAll(o) { + return o.a + o.b + o.c + o.d + o.e + o.f + o.g + o.h; +} +bench("call 8-field fn", function(n) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8 }; + let sum = 0; + for (let i = 0; i < n; i++) sum += readAll(o); + return sum; +}, 2000000); diff --git a/tests/bench_gc.js b/tests/bench_gc.js index 8fd1b24..d4b6763 100644 --- a/tests/bench_gc.js +++ b/tests/bench_gc.js @@ -79,7 +79,6 @@ function showStats(label, s) { console.log(` arenaSize: ${fmt(s.arenaSize)}`); console.log(` external.buffers: ${fmt(s.external.buffers)}`); console.log(` external.code: ${fmt(s.external.code)}`); - console.log(` external.collections: ${fmt(s.external.collections)}`); console.log(` external.total: ${fmt(s.external.total)}`); console.log(` cstack: ${fmt(s.cstack)}`); console.log(` rss: ${fmt(s.rss)}`); diff --git a/tests/bench_gc_mark_func.js b/tests/bench_gc_mark_func.js new file mode 100644 index 0000000..55d0089 --- /dev/null +++ b/tests/bench_gc_mark_func.js @@ -0,0 +1,124 @@ +const now = () => (typeof performance !== 'undefined' ? performance.now() : Date.now()); + +function makeChildHeavyFunction(childCount) { + let src = 'return function run(seed) {\n'; + for (let i = 0; i < childCount; i++) { + src += `function child_${i}(x) { return x + ${i}; }\n`; + } + src += 'let acc = seed;\n'; + for (let i = 0; i < childCount; i++) { + src += `acc = child_${i}(acc);\n`; + } + src += 'return acc;\n'; + src += '}'; + return new Function(src)(); +} + +function makeTemplateHeavyFunction(siteCount) { + let src = 'function tag(strs, ...args) { return strs.length + args.length; }\n'; + src += 'return function run(seed) {\n'; + src += 'let acc = 0;\n'; + for (let i = 0; i < siteCount; i++) { + src += `acc += tag\`${i}:\${seed}:${i}\`;\n`; + } + src += 'return acc;\n'; + src += '}'; + return new Function(src)(); +} + +function churnGarbage(rounds, width) { + const ring = new Array(8); + for (let r = 0; r < rounds; r++) { + const batch = new Array(width); + for (let i = 0; i < width; i++) { + batch[i] = { + i, + text: 'x' + i, + arr: [i, i + 1, i + 2, i + 3] + }; + } + ring[r & 7] = batch; + } + return ring.length; +} + +function snapshot() { + return Ant.raw.gcMarkProfile(); +} + +function diff(after, before) { + return { + collections: after.collections - before.collections, + funcVisits: after.funcVisits - before.funcVisits, + childEdges: after.childEdges - before.childEdges, + constSlots: after.constSlots - before.constSlots, + timeMs: after.timeMs - before.timeMs + }; +} + +function runCase(name, factory, opts = {}) { + const fnCount = opts.fnCount ?? 24; + const rounds = opts.rounds ?? 24; + const garbageRounds = opts.garbageRounds ?? 64; + const garbageWidth = opts.garbageWidth ?? 192; + + const fns = []; + for (let i = 0; i < fnCount; i++) fns.push(factory()); + + Ant.raw.gcMarkProfileReset(); + const before = snapshot(); + + let checksum = 0; + const start = now(); + for (let r = 0; r < rounds; r++) { + for (let i = 0; i < fns.length; i++) checksum += fns[i](r); + churnGarbage(garbageRounds, garbageWidth); + } + const elapsed = now() - start; + + const after = snapshot(); + const stats = diff(after, before); + + console.log(`\n[${name}]`); + console.log(`elapsed_ms=${elapsed.toFixed(2)}`); + console.log(`collections=${stats.collections}`); + console.log(`func_visits=${stats.funcVisits}`); + console.log(`child_edges=${stats.childEdges}`); + console.log(`const_slots=${stats.constSlots}`); + console.log(`mark_func_ms=${stats.timeMs.toFixed(3)}`); + console.log(`checksum=${checksum}`); +} + +Ant.raw.gcMarkProfileEnable(true); +Ant.raw.gcMarkProfileReset(); + +runCase('child-func-heavy', () => makeChildHeavyFunction(160), { + fnCount: 20, + rounds: 22, + garbageRounds: 72, + garbageWidth: 224 +}); + +runCase('template-slot-heavy', () => makeTemplateHeavyFunction(160), { + fnCount: 20, + rounds: 22, + garbageRounds: 72, + garbageWidth: 224 +}); + +runCase( + 'mixed', + () => { + const childHeavy = makeChildHeavyFunction(96); + const templateHeavy = makeTemplateHeavyFunction(96); + return function run(seed) { + return childHeavy(seed) + templateHeavy(seed); + }; + }, + { + fnCount: 20, + rounds: 22, + garbageRounds: 72, + garbageWidth: 224 + } +); diff --git a/tests/bench_inobj_props.js b/tests/bench_inobj_props.js new file mode 100644 index 0000000..48e7eca --- /dev/null +++ b/tests/bench_inobj_props.js @@ -0,0 +1,178 @@ +function nowMs() { + if (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') { + return performance.now(); + } + return Date.now(); +} + +const BENCH_WARMUP_RUNS = 2; +const BENCH_SAMPLE_RUNS = 7; + +function percentile(sorted, p) { + if (sorted.length === 0) return 0; + if (sorted.length === 1) return sorted[0]; + const pos = (sorted.length - 1) * p; + const base = Math.floor(pos); + const frac = pos - base; + const next = sorted[base + 1]; + if (next === undefined) return sorted[base]; + return sorted[base] + (next - sorted[base]) * frac; +} + +function stableNumericSort(values) { + const out = values.slice(); + for (let i = 1; i < out.length; i++) { + const x = out[i]; + let j = i - 1; + while (j >= 0 && out[j] > x) { + out[j + 1] = out[j]; + j--; + } + out[j + 1] = x; + } + return out; +} + +function bench(label, fn, iters) { + const warmupIters = Math.max(100_000, (iters / 4) | 0); + for (let i = 0; i < BENCH_WARMUP_RUNS; i++) fn(warmupIters); + + let result = 0; + const samples = []; + for (let i = 0; i < BENCH_SAMPLE_RUNS; i++) { + const t0 = nowMs(); + const r = fn(iters); + const dt = nowMs() - t0; + if (i === 0) result = r; + samples.push(dt); + } + + const sorted = stableNumericSort(samples); + const min = sorted[0]; + const med = percentile(sorted, 0.5); + const p95 = percentile(sorted, 0.95); + const max = sorted[sorted.length - 1]; + const opsPerMs = med > 0 ? (iters / med).toFixed(2) : 'inf'; + + console.log( + label + + ': median ' + med.toFixed(2) + 'ms (' + opsPerMs + ' ops/ms)' + + ', p95 ' + p95.toFixed(2) + 'ms' + + ', min ' + min.toFixed(2) + 'ms' + + ', max ' + max.toFixed(2) + 'ms' + + ' result=' + result + ); +} + +function initObj(o, n, base) { + if (n >= 1) o.a = 1 + base; + if (n >= 2) o.b = 2 + base; + if (n >= 3) o.c = 3 + base; + if (n >= 4) o.d = 4 + base; + if (n >= 5) o.e = 5 + base; + if (n >= 6) o.f = 6 + base; + if (n >= 7) o.g = 7 + base; + if (n >= 8) o.h = 8 + base; +} + +function makeObj(n, base) { + const o = {}; + initObj(o, n, base || 0); + return o; +} + +function readLast(obj, n, iters) { + let sum = 0; + switch (n) { + case 1: for (let i = 0; i < iters; i++) sum += obj.a; break; + case 4: for (let i = 0; i < iters; i++) sum += obj.d; break; + case 5: for (let i = 0; i < iters; i++) sum += obj.e; break; + case 8: for (let i = 0; i < iters; i++) sum += obj.h; break; + } + return sum; +} + +function writeLast(obj, n, iters) { + let sum = 0; + switch (n) { + case 1: + for (let i = 0; i < iters; i++) { obj.a = i; sum += obj.a; } + break; + case 4: + for (let i = 0; i < iters; i++) { obj.d = i; sum += obj.d; } + break; + case 5: + for (let i = 0; i < iters; i++) { obj.e = i; sum += obj.e; } + break; + case 8: + for (let i = 0; i < iters; i++) { obj.h = i; sum += obj.h; } + break; + } + return sum; +} + +function readFirst(obj, iters) { + let sum = 0; + for (let i = 0; i < iters; i++) sum += obj.a; + return sum; +} + +function createAndTouch(n, iters) { + let sum = 0; + for (let i = 0; i < iters; i++) { + const o = {}; + initObj(o, n, i & 7); + sum += o.a; + } + return sum; +} + +function polyObjs5() { + const o1 = {}; + o1.a = 1; o1.b = 2; o1.c = 3; o1.d = 4; o1.e = 5; + const o2 = {}; + o2.b = 2; o2.a = 1; o2.c = 3; o2.d = 4; o2.e = 5; + const o3 = {}; + o3.c = 3; o3.b = 2; o3.a = 1; o3.d = 4; o3.e = 5; + const o4 = {}; + o4.d = 4; o4.c = 3; o4.b = 2; o4.a = 1; o4.e = 5; + return [o1, o2, o3, o4]; +} + +function polyObjs8() { + const o1 = makeObj(8, 0); + const o2 = {}; + o2.b = 2; o2.a = 1; o2.c = 3; o2.d = 4; o2.e = 5; o2.f = 6; o2.g = 7; o2.h = 8; + const o3 = {}; + o3.c = 3; o3.b = 2; o3.a = 1; o3.d = 4; o3.e = 5; o3.f = 6; o3.g = 7; o3.h = 8; + const o4 = {}; + o4.d = 4; o4.c = 3; o4.b = 2; o4.a = 1; o4.e = 5; o4.f = 6; o4.g = 7; o4.h = 8; + return [o1, o2, o3, o4]; +} + +function readPolyA(objs, iters) { + let sum = 0; + for (let i = 0; i < iters; i++) sum += objs[i & 3].a; + return sum; +} + +function runSize(n, readIters, writeIters, createIters) { + const obj = makeObj(n, 0); + console.log('\n=== props=' + n + ' ==='); + console.log('sanity a=' + obj.a); + + bench('read_first', iters => readFirst(obj, iters), readIters); + bench('read_last', iters => readLast(obj, n, iters), readIters); + bench('write_last', iters => writeLast(obj, n, iters), writeIters); + bench('create_' + n, iters => createAndTouch(n, iters), createIters); +} + +console.log('in-object own-property benchmark'); +runSize(1, 4_000_000, 2_000_000, 500_000); +runSize(4, 4_000_000, 2_000_000, 500_000); +runSize(5, 4_000_000, 2_000_000, 500_000); +runSize(8, 4_000_000, 2_000_000, 500_000); + +console.log('\n=== polymorphic ==='); +bench('poly_read_a_5', iters => readPolyA(polyObjs5(), iters), 4_000_000); +bench('poly_read_a_8', iters => readPolyA(polyObjs8(), iters), 4_000_000); diff --git a/tests/bench_lkp.js b/tests/bench_lkp.js new file mode 100644 index 0000000..54863a2 --- /dev/null +++ b/tests/bench_lkp.js @@ -0,0 +1,69 @@ +// Benchmark: property read paths (lkp_val vs lkp + propref) + +function bench(name, fn, iterations) { + const start = Date.now(); + fn(iterations); + const elapsed = Date.now() - start; + console.log(name + ": " + elapsed + "ms (" + iterations + " iters, " + (elapsed / iterations * 1000).toFixed(2) + "µs/op)"); +} + +// 1. Global variable read (sv_global_get_interned) +var gval = 42; +bench("global read", function(n) { + let sum = 0; + for (let i = 0; i < n; i++) sum += gval; + return sum; +}, 5000000); + +// 2. Object property read +bench("prop read", function(n) { + let o = { x: 1, y: 2, z: 3 }; + let sum = 0; + for (let i = 0; i < n; i++) sum += o.x + o.y + o.z; + return sum; +}, 5000000); + +// 3. Prototype chain lookup +bench("proto chain read", function(n) { + function Foo() {} + let o = new Foo(); + let sum = 0; + for (let i = 0; i < n; i++) sum += o.constructor ? 1 : 0; + return sum; +}, 1000000); + +// 4. Array .length read +bench("array length read", function(n) { + let arr = [1, 2, 3, 4, 5]; + let sum = 0; + for (let i = 0; i < n; i++) sum += arr.length; + return sum; +}, 5000000); + +// 5. Function .name read +bench("func name read", function(n) { + function myFunc() {} + let count = 0; + for (let i = 0; i < n; i++) count += myFunc.name.length; + return count; +}, 1000000); + +// 6. Repeated prop reads on large object +bench("10-prop obj repeated read", function(n) { + let o = { a: 1, b: 2, c: 3, d: 4, e: 5, f: 6, g: 7, h: 8, i: 9, j: 10 }; + let sum = 0; + for (let i = 0; i < n; i++) { + sum += o.a + o.b + o.c + o.d + o.e + o.f + o.g + o.h + o.i + o.j; + } + return sum; +}, 1000000); + +// 7. Constructor prototype lookup (string method) +bench("ctor proto lookup", function(n) { + let sum = 0; + for (let i = 0; i < n; i++) { + let s = "hello"; + sum += s.length; + } + return sum; +}, 5000000); diff --git a/tests/bench_map_set.js b/tests/bench_map_set.js new file mode 100644 index 0000000..d1a0cbb --- /dev/null +++ b/tests/bench_map_set.js @@ -0,0 +1,35 @@ +var N = 100000; + +var m = new Map(); +var start = Date.now(); +for (var i = 0; i < N; i++) { + m.set('key' + i, i); +} +var end = Date.now(); +console.log('100k Map.set:', end - start, 'ms'); + +start = Date.now(); +var sum = 0; +for (var i = 0; i < N; i++) { + sum += m.get('key' + i); +} +end = Date.now(); +console.log('100k Map.get:', end - start, 'ms'); +console.log('sum:', sum); + +var s = new Set(); +start = Date.now(); +for (var i = 0; i < N; i++) { + s.add('val' + i); +} +end = Date.now(); +console.log('100k Set.add:', end - start, 'ms'); + +start = Date.now(); +var hits = 0; +for (var i = 0; i < N; i++) { + if (s.has('val' + i)) hits++; +} +end = Date.now(); +console.log('100k Set.has:', end - start, 'ms'); +console.log('hits:', hits); diff --git a/tests/bench_prototype_chain.js b/tests/bench_prototype_chain.js index 94e61bd..ad00d0f 100644 --- a/tests/bench_prototype_chain.js +++ b/tests/bench_prototype_chain.js @@ -1,7 +1,38 @@ function nowMs() { + if (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') { + return performance.now(); + } return Date.now(); } +const BENCH_WARMUP_RUNS = 2; +const BENCH_SAMPLE_RUNS = 7; + +function percentile(sorted, p) { + if (sorted.length === 0) return 0; + if (sorted.length === 1) return sorted[0]; + const pos = (sorted.length - 1) * p; + const base = Math.floor(pos); + const frac = pos - base; + const next = sorted[base + 1]; + if (next === undefined) return sorted[base]; + return sorted[base] + (next - sorted[base]) * frac; +} + +function stableNumericSort(values) { + const out = values.slice(); + for (let i = 1; i < out.length; i++) { + const x = out[i]; + let j = i - 1; + while (j >= 0 && out[j] > x) { + out[j + 1] = out[j]; + j--; + } + out[j + 1] = x; + } + return out; +} + function makeChain(depth) { function C() {} const root = { marker: 1 }; @@ -13,11 +44,34 @@ function makeChain(depth) { } function bench(label, fn, iters) { - const t0 = nowMs(); - const result = fn(iters); - const dt = nowMs() - t0; - const opsPerMs = dt > 0 ? (iters / dt).toFixed(2) : 'inf'; - console.log(label + ': ' + dt + 'ms (' + opsPerMs + ' ops/ms) result=' + result); + const warmupIters = Math.max(100_000, (iters / 4) | 0); + for (let i = 0; i < BENCH_WARMUP_RUNS; i++) fn(warmupIters); + + let result = 0; + const samples = []; + for (let i = 0; i < BENCH_SAMPLE_RUNS; i++) { + const t0 = nowMs(); + const r = fn(iters); + const dt = nowMs() - t0; + if (i === 0) result = r; + samples.push(dt); + } + + const sorted = stableNumericSort(samples); + const min = sorted[0]; + const med = percentile(sorted, 0.5); + const p95 = percentile(sorted, 0.95); + const max = sorted[sorted.length - 1]; + const opsPerMs = med > 0 ? (iters / med).toFixed(2) : 'inf'; + + console.log( + label + + ': median ' + med.toFixed(2) + 'ms (' + opsPerMs + ' ops/ms)' + + ', p95 ' + p95.toFixed(2) + 'ms' + + ', min ' + min.toFixed(2) + 'ms' + + ', max ' + max.toFixed(2) + 'ms' + + ' result=' + result + ); } function runDepth(depth) { diff --git a/tests/test_ctor_prop_feedback.cjs b/tests/test_ctor_prop_feedback.cjs new file mode 100644 index 0000000..c864ec6 --- /dev/null +++ b/tests/test_ctor_prop_feedback.cjs @@ -0,0 +1,52 @@ +function assert(cond, msg) { + if (!cond) throw new Error(msg || 'assertion failed'); +} + +function C5() { + this.a = 1; + this.b = 2; + this.c = 3; + this.d = 4; + this.e = 5; +} + +function C2() { + this.a = 1; + this.b = 2; +} + +function CWarm() { + this.a = 1; + this.b = 2; + this.c = 3; +} + +function CEmpty() {} + +for (let i = 0; i < 8; i++) new CWarm(); +for (let i = 0; i < 50; i++) new C2(); +for (let i = 0; i < 1000; i++) new C5(); + +const fEmpty = Ant.raw.ctorPropFeedback(CEmpty); +const fWarm = Ant.raw.ctorPropFeedback(CWarm); +const f2 = Ant.raw.ctorPropFeedback(C2); +const f5 = Ant.raw.ctorPropFeedback(C5); + +assert(fEmpty && fWarm && f2 && f5, 'feedback object missing'); +assert(fWarm.inobjLimitFrozen === false, 'CWarm should still be in slack tracking'); +assert(fWarm.slackRemaining > 0, 'CWarm slackRemaining should be positive'); +assert(fWarm.inobjLimit === fEmpty.inobjLimit, 'CWarm should use default inobjLimit before freeze'); +assert(f2.samples === 50, 'C2 sample count mismatch'); +assert(f5.samples === 1000, 'C5 sample count mismatch'); +assert(Array.isArray(f2.bins) && Array.isArray(f5.bins), 'bins missing'); +assert(f2.bins[2] === 50, 'C2 bin[2] mismatch'); +assert(f5.bins[5] === 1000, 'C5 bin[5] mismatch'); +assert(f5.overflowFrom === 16, 'overflowFrom mismatch'); +assert(f2.inobjLimit === 2, 'C2 inobjLimit mismatch'); +assert(f5.inobjLimit >= 4, 'C5 inobjLimit mismatch'); +assert(f2.inobjLimitFrozen === true, 'C2 limit should be frozen'); +assert(f5.inobjLimitFrozen === true, 'C5 limit should be frozen'); +assert(f2.slackRemaining === 0, 'C2 slackRemaining mismatch'); +assert(f5.slackRemaining === 0, 'C5 slackRemaining mismatch'); + +console.log('ctorPropFeedback OK'); diff --git a/tests/test_gc_async.js b/tests/test_gc_async.js index 3850bf3..c3cf5ea 100644 --- a/tests/test_gc_async.js +++ b/tests/test_gc_async.js @@ -37,14 +37,12 @@ async function handleEvent(_) { for (let count = 1; count <= 500; count++) { handleEvent(count); - const stats = Ant.stats(); + const s = Ant.stats(), a = s.alloc, M = 1024 * 1024; + const total = (s.pools.totalUsed + a.total) / M; console.log( - `tick ${count}: arena ${(stats.arenaUsed / 1024 / 1024).toFixed(1)}MB / ` + - `${(stats.arenaSize / 1024 / 1024).toFixed(1)}MB, ` + - `rss ${(stats.rss / 1024 / 1024).toFixed(1)}MB` + `tick ${count}: pools ${(s.pools.totalUsed/M).toFixed(1)}MB obj ${(a.objects/M).toFixed(1)}MB shp ${(a.shapes/M).toFixed(1)}MB arr ${(a.arrays/M).toFixed(1)}MB refs ${(a.propRefs/M).toFixed(1)}MB cls ${(a.closures/M).toFixed(1)}MB uv ${(a.upvalues/M).toFixed(1)}MB ov ${(a.overflow/M).toFixed(1)}MB | ${total.toFixed(1)}MB rss ${(s.rss/M).toFixed(1)}MB` ); } -console.log('Done - forcing GC...'); -const after = Ant.stats(); -console.log(`after GC: arena ${(after.arenaUsed / 1024 / 1024).toFixed(1)}MB`); +const a = Ant.stats(); +console.log(`done: total ${((a.pools.totalUsed + a.alloc.total) / 1024 / 1024).toFixed(1)}MB`); diff --git a/tests/test_gc_coro.js b/tests/test_gc_coro.js index f15efb0..151c6c1 100644 --- a/tests/test_gc_coro.js +++ b/tests/test_gc_coro.js @@ -46,17 +46,17 @@ const interval = setInterval(() => { count++; handleEvent(count); - const stats = Ant.stats(); + const s = Ant.stats(), a = s.alloc, M = 1024 * 1024; + const total = (s.pools.totalUsed + a.total) / M; console.log( - `tick ${count}: arena ${(stats.arenaUsed / 1024 / 1024).toFixed(1)}MB / ${(stats.arenaSize / 1024 / 1024).toFixed(1)}MB, rss ${(stats.rss / 1024 / 1024).toFixed(1)}MB` + `tick ${count}: pools ${(s.pools.totalUsed/M).toFixed(1)}MB obj ${(a.objects/M).toFixed(1)}MB shp ${(a.shapes/M).toFixed(1)}MB arr ${(a.arrays/M).toFixed(1)}MB refs ${(a.propRefs/M).toFixed(1)}MB cls ${(a.closures/M).toFixed(1)}MB uv ${(a.upvalues/M).toFixed(1)}MB ov ${(a.overflow/M).toFixed(1)}MB | ${total.toFixed(1)}MB rss ${(s.rss/M).toFixed(1)}MB` ); if (count >= 500) { - // Run for ~5 seconds clearInterval(interval); setTimeout(() => { - const after = Ant.stats(); - console.log(`after GC: arena ${(after.arenaUsed / 1024 / 1024).toFixed(1)}MB`); + const a = Ant.stats(); + console.log(`done: total ${((a.pools.totalUsed + a.alloc.total) / 1024 / 1024).toFixed(1)}MB`); }, 10); } }, 10); // 500 * 10ms = 5 seconds diff --git a/tests/test_gc_stress10.js b/tests/test_gc_stress10.js index 066f7b8..025f560 100644 --- a/tests/test_gc_stress10.js +++ b/tests/test_gc_stress10.js @@ -27,15 +27,15 @@ for (let frame = 0; frame < 50000; frame++) { render(); const stats = Ant.stats(); const elapsed = ((Date.now() - t0) / 1000).toFixed(2); + const a = stats.alloc, M = 1024 * 1024; + const total = (stats.pools.totalUsed + a.total) / M; console.log( - `frame ${frame}: arena ${(stats.arenaUsed / 1024 / 1024).toFixed(1)}MB / ` + - `${(stats.arenaSize / 1024 / 1024).toFixed(1)}MB, ` + - `rss ${(stats.rss / 1024 / 1024).toFixed(1)}MB, ` + - `elapsed ${elapsed}s` + `frame ${frame}: pools ${(stats.pools.totalUsed/M).toFixed(1)}MB obj ${(a.objects/M).toFixed(1)}MB shp ${(a.shapes/M).toFixed(1)}MB arr ${(a.arrays/M).toFixed(1)}MB refs ${(a.propRefs/M).toFixed(1)}MB cls ${(a.closures/M).toFixed(1)}MB uv ${(a.upvalues/M).toFixed(1)}MB ov ${(a.overflow/M).toFixed(1)}MB | ${total.toFixed(1)}MB rss ${(stats.rss/M).toFixed(1)}MB ${elapsed}s` ); } const total = ((Date.now() - t0) / 1000).toFixed(2); -const final_stats = Ant.stats(); -console.log(`Done: ${total}s, arena ${(final_stats.arenaUsed / 1024 / 1024).toFixed(1)}MB, ` + `rss ${(final_stats.rss / 1024 / 1024).toFixed(1)}MB`); +const f = Ant.stats(); +const fmem = (f.pools.totalUsed + f.alloc.total) / 1024 / 1024; +console.log(`Done: ${total}s, total ${fmem.toFixed(1)}MB, rss ${(f.rss / 1024 / 1024).toFixed(1)}MB`); console.log('OK'); diff --git a/tests/test_gc_timer.js b/tests/test_gc_timer.js index bd77f1c..894245e 100644 --- a/tests/test_gc_timer.js +++ b/tests/test_gc_timer.js @@ -42,16 +42,15 @@ const interval = setInterval(() => { count++; handleEvent(count); - const stats = Ant.stats(); + const s = Ant.stats(), a = s.alloc, M = 1024 * 1024; + const total = (s.pools.totalUsed + a.total) / M; console.log( - `tick ${count}: arena ${(stats.arenaUsed / 1024 / 1024).toFixed(1)}MB / ` + - `${(stats.arenaSize / 1024 / 1024).toFixed(1)}MB, ` + - `rss ${(stats.rss / 1024 / 1024).toFixed(1)}MB` + `tick ${count}: pools ${(s.pools.totalUsed/M).toFixed(1)}MB obj ${(a.objects/M).toFixed(1)}MB shp ${(a.shapes/M).toFixed(1)}MB arr ${(a.arrays/M).toFixed(1)}MB refs ${(a.propRefs/M).toFixed(1)}MB cls ${(a.closures/M).toFixed(1)}MB uv ${(a.upvalues/M).toFixed(1)}MB ov ${(a.overflow/M).toFixed(1)}MB | ${total.toFixed(1)}MB rss ${(s.rss/M).toFixed(1)}MB` ); if (count >= 500) { clearInterval(interval); - const after = Ant.stats(); - console.log(`after GC: arena ${(after.arenaUsed / 1024 / 1024).toFixed(1)}MB`); + const a = Ant.stats(); + console.log(`done: total ${((a.pools.totalUsed + a.alloc.total) / 1024 / 1024).toFixed(1)}MB`); } }, 10); diff --git a/tests/test_jit_post_inc_upval.js b/tests/test_jit_post_inc_upval.js new file mode 100644 index 0000000..e34b26c --- /dev/null +++ b/tests/test_jit_post_inc_upval.js @@ -0,0 +1,31 @@ +function makeIter() { + let n = 0; + return function iter() { + const prev = n++; + return prev; + }; +} + +const iter = makeIter(); +let sum = 0; +const rounds = 5000; + +for (let i = 0; i < rounds; i++) { + sum += iter(); +} + +const expected = (rounds - 1) * rounds / 2; +if (sum !== expected) { + throw new Error( + "post-inc sum mismatch: got " + sum + ", expected " + expected + ); +} + +const next = iter(); +if (next !== rounds) { + throw new Error( + "post-inc next mismatch: got " + next + ", expected " + rounds + ); +} + +console.log("OK: test_jit_post_inc_upval"); diff --git a/tests/test_label_of.js b/tests/test_label_of.js new file mode 100644 index 0000000..034a8c9 --- /dev/null +++ b/tests/test_label_of.js @@ -0,0 +1,68 @@ +// Regression test: contextual keywords (of, as, from, async) used as labels +console.log('=== Label with contextual keywords ==='); + +// Test 1: "of" as a label in a method body +let result1 = 0; +const obj1 = { + b() { + of: { + result1 = 1; + break of; + result1 = 99; + } + } +}; +obj1.b(); +console.log('Test 1 (of label in method): ' + result1); +console.log('Expected: 1'); + +// Test 2: "of" as a label in top-level code +let result2 = 0; +of: { + result2 = 2; + break of; + result2 = 99; +} +console.log('Test 2 (of label top-level): ' + result2); +console.log('Expected: 2'); + +// Test 3: "as" as a label +let result3 = 0; +as: { + result3 = 3; + break as; + result3 = 99; +} +console.log('Test 3 (as label): ' + result3); +console.log('Expected: 3'); + +// Test 4: "from" as a label +let result4 = 0; +from: { + result4 = 4; + break from; + result4 = 99; +} +console.log('Test 4 (from label): ' + result4); +console.log('Expected: 4'); + +// Test 5: "async" as a label +let result5 = 0; +async: { + result5 = 5; + break async; + result5 = 99; +} +console.log('Test 5 (async label): ' + result5); +console.log('Expected: 5'); + +// Test 6: "of" as a label on a loop +let result6 = ''; +of: for (let i = 0; i < 5; i++) { + if (i === 3) break of; + result6 += i; +} +console.log('Test 6 (of label on loop): ' + result6); +console.log('Expected: 012'); + +console.log('\n=== All contextual keyword label tests completed ==='); diff --git a/tests/test_prototype_chain_class_mutation.cjs b/tests/test_prototype_chain_class_mutation.cjs new file mode 100644 index 0000000..b9abbc4 --- /dev/null +++ b/tests/test_prototype_chain_class_mutation.cjs @@ -0,0 +1,329 @@ +// bench_prototype_dynamic.js +// Tests whether ant's O(1) instanceof breaks under dynamic mutations. +// +// Key finding from baseline: instanceof is O(1) but isPrototypeOf is O(n). +// This means ant likely stores type/class metadata separately from the +// prototype chain. These tests try to break that. + +const ITERATIONS = 2000000; + +function buildChain(depth) { + let chain = [class Base {}]; + for (let i = 1; i < depth; i++) { + chain.push(class extends chain[i - 1] {}); + } + return chain; +} + +function median(arr) { + arr.sort((a, b) => a - b); + const mid = arr.length >> 1; + return arr.length % 2 ? arr[mid] : (arr[mid - 1] + arr[mid]) / 2; +} + +function bench(label, fn, iterations = ITERATIONS) { + for (let i = 0; i < 1000; i++) fn(); + + const samples = []; + for (let run = 0; run < 5; run++) { + const start = performance.now(); + for (let i = 0; i < iterations; i++) fn(); + samples.push(performance.now() - start); + } + + const med = median(samples); + const ops = ((iterations / med) * 1000).toFixed(2); + const p95 = samples.sort((a, b) => a - b)[Math.floor(samples.length * 0.95)]; + console.log( + ` ${label}: median ${med.toFixed(2)}ms (${ops} ops/ms), ` + + `p95 ${p95.toFixed(2)}ms, min ${Math.min(...samples).toFixed(2)}ms, ` + + `max ${Math.max(...samples).toFixed(2)}ms result=${iterations}` + ); + return med; +} + +// ============================================================ +// Test 1: Baseline — confirm instanceof O(1), isPrototypeOf O(n) +// ============================================================ +console.log('=== Test 1: Static chain (baseline) ==='); +for (const depth of [8, 24, 40]) { + const chain = buildChain(depth); + const Leaf = chain[chain.length - 1]; + const Root = chain[0]; + const obj = new Leaf(); + + console.log(`\n--- depth=${depth} ---`); + bench('instanceof', () => obj instanceof Root); +} + +// ============================================================ +// Test 2: setPrototypeOf — reparent an object to a different chain +// Creates two separate chains, then moves an object from one +// to the other. If ant caches "is instance of Root" at creation +// time, the result will be wrong after the reparent. +// ============================================================ +console.log('\n=== Test 2: setPrototypeOf — reparent to different chain ==='); +{ + const chainA = buildChain(24); + const chainB = buildChain(24); + const LeafA = chainA[chainA.length - 1]; + const RootA = chainA[0]; + const RootB = chainB[0]; + + const obj = new LeafA(); + + const beforeA = obj instanceof RootA; // should be true + const beforeB = obj instanceof RootB; // should be false + + // reparent: point obj's proto to chainB's leaf prototype instead + Object.setPrototypeOf(obj, chainB[chainB.length - 1].prototype); + + const afterA = obj instanceof RootA; // should be FALSE now + const afterB = obj instanceof RootB; // should be TRUE now + + console.log(` before: instanceofA=${beforeA}(expect true), instanceofB=${beforeB}(expect false)`); + console.log(` after: instanceofA=${afterA}(expect false), instanceofB=${afterB}(expect true)`); + + if (afterA !== false || afterB !== true) { + console.log(' *** CORRECTNESS BUG: instanceof returned wrong result after setPrototypeOf ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof reparented obj vs RootB', () => obj instanceof RootB); + bench('instanceof reparented obj vs RootA', () => obj instanceof RootA); +} + +// ============================================================ +// Test 3: setPrototypeOf on a mid-chain prototype +// Detach the middle of a chain and reattach to a different root. +// All objects below the splice point should stop being instanceof +// the original root. +// ============================================================ +console.log('\n=== Test 3: Splice mid-chain prototype to new root ==='); +{ + const depth = 24; + const chain = buildChain(depth); + const Leaf = chain[chain.length - 1]; + const Root = chain[0]; + const obj = new Leaf(); + + const midIdx = Math.floor(depth / 2); + const before = obj instanceof Root; // true + + // create a totally separate root + class NewRoot {} + + // detach mid-chain from old chain, attach to NewRoot + Object.setPrototypeOf(chain[midIdx].prototype, NewRoot.prototype); + + const afterOldRoot = obj instanceof Root; // should be FALSE + const afterNewRoot = obj instanceof NewRoot; // should be TRUE + + console.log(` before: instanceof Root=${before}(expect true)`); + console.log(` after: instanceof Root=${afterOldRoot}(expect false), instanceof NewRoot=${afterNewRoot}(expect true)`); + + if (afterOldRoot !== false || afterNewRoot !== true) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof (post-splice, vs NewRoot)', () => obj instanceof NewRoot); + bench('instanceof (post-splice, vs old Root)', () => obj instanceof Root); +} + +// ============================================================ +// Test 4: Constructor.prototype reassignment +// Replace .prototype entirely. Old objects should no longer +// be instanceof, new objects should be. +// ============================================================ +console.log('\n=== Test 4: Constructor.prototype reassignment ==='); +{ + class Foo {} + const obj1 = new Foo(); + const before = obj1 instanceof Foo; // true + + // nuke it + Foo.prototype = { constructor: Foo }; + + const obj1After = obj1 instanceof Foo; // should be FALSE + const obj2 = new Foo(); + const obj2After = obj2 instanceof Foo; // should be TRUE + + console.log(` obj1 before=${before}(expect true), obj1 after=${obj1After}(expect false), obj2=${obj2After}(expect true)`); + + if (obj1After !== false || obj2After !== true) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 5: Symbol.hasInstance override +// Completely bypasses the prototype chain walk per spec. +// ============================================================ +console.log('\n=== Test 5: Symbol.hasInstance ==='); +{ + class Foo {} + const obj = new Foo(); + + // default behavior + const normalResult = obj instanceof Foo; // true + const normalFake = {} instanceof Foo; // false + + // override: always true + Object.defineProperty(Foo, Symbol.hasInstance, { + value: instance => true, + configurable: true + }); + + const overrideResult = {} instanceof Foo; // should be TRUE now + + console.log(` normal: obj=${normalResult}(expect true), {}=${normalFake}(expect false)`); + console.log(` override: {} instanceof Foo = ${overrideResult}(expect true)`); + + if (overrideResult !== true) { + console.log(' *** Symbol.hasInstance NOT RESPECTED ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof (Symbol.hasInstance always-true)', () => ({}) instanceof Foo); + + // override: custom logic + let callCount = 0; + Object.defineProperty(Foo, Symbol.hasInstance, { + value: instance => { + callCount++; + return instance.special === true; + }, + configurable: true + }); + + const customA = { special: true } instanceof Foo; // true + const customB = { special: false } instanceof Foo; // false + + console.log(` custom: {special:true}=${customA}(expect true), {special:false}=${customB}(expect false)`); + console.log(` Symbol.hasInstance was called ${callCount} times`); + + if (customA !== true || customB !== false) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 6: Megamorphic — many different shapes through same check +// ============================================================ +console.log('\n=== Test 6: Megamorphic instanceof ==='); +{ + const depth = 16; + const chain = buildChain(depth); + const Root = chain[0]; + + const objects = []; + for (let i = 0; i < 100; i++) { + const obj = new chain[chain.length - 1](); + obj['prop_' + i] = i; + obj['extra_' + i * 7] = true; + objects.push(obj); + } + + let idx = 0; + bench('instanceof (100 shapes)', () => { + idx = (idx + 1) % objects.length; + return objects[idx] instanceof Root; + }); +} + +// ============================================================ +// Test 7: Prototype swap every iteration (the nuclear option) +// ============================================================ +console.log('\n=== Test 7: Prototype swap every iteration ==='); +{ + const depth = 16; + const chain = buildChain(depth); + const Leaf = chain[chain.length - 1]; + const Root = chain[0]; + + class Alt {} + const midIdx = Math.floor(depth / 2); + const originalProto = Object.getPrototypeOf(chain[midIdx].prototype); + + let toggle = false; + bench( + 'instanceof (swap each iter)', + () => { + toggle = !toggle; + if (toggle) { + Object.setPrototypeOf(chain[midIdx].prototype, Alt.prototype); + } else { + Object.setPrototypeOf(chain[midIdx].prototype, originalProto); + } + return new Leaf() instanceof Root; + }, + 100000 + ); +} + +// ============================================================ +// Test 8: Cross-realm-like — objects from different "worlds" +// Simulates the cross-realm problem where Array from iframe +// isn't instanceof Array from parent. Tests if ant has this +// same behavior (it should, per spec). +// ============================================================ +console.log('\n=== Test 8: Unrelated classes with same structure ==='); +{ + class FooA { + method() { + return 1; + } + } + class FooB { + method() { + return 1; + } + } + + const objA = new FooA(); + const crossCheck = objA instanceof FooB; // should be FALSE + + console.log(` FooA instance instanceof FooB = ${crossCheck}(expect false)`); + + if (crossCheck !== false) { + console.log(' *** CORRECTNESS BUG: unrelated classes should not match ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 9: prop_lookup with dynamic shadowing/unshadowing +// ============================================================ +console.log('\n=== Test 9: prop_lookup with prototype mutation ==='); +for (const depth of [8, 24, 40]) { + const chain = buildChain(depth); + const Root = chain[0]; + Root.prototype.marker = 1; + const Leaf = chain[chain.length - 1]; + const obj = new Leaf(); + + console.log(`\n--- depth=${depth} ---`); + console.log(` sanity marker=${obj.marker}`); + + const midIdx = Math.floor(depth / 2); + chain[midIdx].prototype.marker = 42; + console.log(` after shadow=${obj.marker} (expected 42)`); + + bench('prop_lookup (shadowed)', () => obj.marker); + + delete chain[midIdx].prototype.marker; + console.log(` after delete=${obj.marker} (expected 1)`); + + bench('prop_lookup (after delete)', () => obj.marker); +} + +console.log('\n=== done ==='); diff --git a/tests/test_prototype_chain_obj_mutation.cjs b/tests/test_prototype_chain_obj_mutation.cjs new file mode 100644 index 0000000..381784d --- /dev/null +++ b/tests/test_prototype_chain_obj_mutation.cjs @@ -0,0 +1,408 @@ +// bench_prototype_plain.js +// Same mutation/correctness tests as bench_prototype_dynamic.js +// but using Object.create chains instead of class extends. +// This targets ant's fast path (~9ms vs ~50ms for classes). + +function nowMs() { + if (typeof performance !== 'undefined' && performance && typeof performance.now === 'function') { + return performance.now(); + } + return Date.now(); +} + +const BENCH_WARMUP_RUNS = 2; +const BENCH_SAMPLE_RUNS = 7; +const ITERATIONS = 2_000_000; + +function percentile(sorted, p) { + if (sorted.length === 0) return 0; + if (sorted.length === 1) return sorted[0]; + const pos = (sorted.length - 1) * p; + const base = Math.floor(pos); + const frac = pos - base; + const next = sorted[base + 1]; + if (next === undefined) return sorted[base]; + return sorted[base] + (next - sorted[base]) * frac; +} + +function stableSort(values) { + const out = values.slice(); + for (let i = 1; i < out.length; i++) { + const x = out[i]; + let j = i - 1; + while (j >= 0 && out[j] > x) { + out[j + 1] = out[j]; + j--; + } + out[j + 1] = x; + } + return out; +} + +function bench(label, fn, iters) { + if (iters === undefined) iters = ITERATIONS; + const warmupIters = Math.max(100000, (iters / 4) | 0); + for (let i = 0; i < BENCH_WARMUP_RUNS; i++) fn(warmupIters); + + const samples = []; + let result = 0; + for (let i = 0; i < BENCH_SAMPLE_RUNS; i++) { + const t0 = nowMs(); + const r = fn(iters); + const dt = nowMs() - t0; + if (i === 0) result = r; + samples.push(dt); + } + + const sorted = stableSort(samples); + const min = sorted[0]; + const med = percentile(sorted, 0.5); + const p95 = percentile(sorted, 0.95); + const max = sorted[sorted.length - 1]; + const opsPerMs = med > 0 ? (iters / med).toFixed(2) : 'inf'; + + console.log( + ' ' + + label + + ': median ' + + med.toFixed(2) + + 'ms (' + + opsPerMs + + ' ops/ms)' + + ', p95 ' + + p95.toFixed(2) + + 'ms' + + ', min ' + + min.toFixed(2) + + 'ms' + + ', max ' + + max.toFixed(2) + + 'ms' + + ' result=' + + result + ); + return med; +} + +// Build a plain Object.create chain of given depth. +// Returns { C, obj, root, chain } +// C = constructor whose .prototype is the root +// root = top prototype (has .marker = 1) +// obj = leaf object, depth levels below root +// chain = array of all prototype objects, chain[0] = root +function buildChain(depth) { + function C() {} + const root = { marker: 1 }; + C.prototype = root; + + const chain = [root]; + let proto = root; + for (let i = 0; i < depth; i++) { + proto = Object.create(proto); + chain.push(proto); + } + const obj = Object.create(proto); + return { C, obj, root, chain }; +} + +// ============================================================ +// Test 1: Static chain baseline +// ============================================================ +console.log('=== Test 1: Static chain (baseline) ==='); +for (const depth of [8, 24, 40]) { + const { C, obj, root } = buildChain(depth); + + console.log('\n--- depth=' + depth + ' ---'); + console.log(' sanity instanceof=' + (obj instanceof C)); + console.log(' sanity isPrototypeOf=' + root.isPrototypeOf(obj)); + console.log(' sanity marker=' + obj.marker); + + bench('instanceof', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (obj instanceof C) c++; + return c; + }); + + bench('isPrototypeOf', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (root.isPrototypeOf(obj)) c++; + return c; + }); + + bench('prop_lookup', function (n) { + let c = 0; + for (let i = 0; i < n; i++) c += obj.marker; + return c; + }); +} + +// ============================================================ +// Test 2: setPrototypeOf — reparent to different chain +// ============================================================ +console.log('\n=== Test 2: setPrototypeOf — reparent to different chain ==='); +{ + const a = buildChain(24); + const b = buildChain(24); + + const obj = a.obj; + const beforeA = obj instanceof a.C; + const beforeB = obj instanceof b.C; + + // reparent obj into chain B + Object.setPrototypeOf(obj, b.chain[b.chain.length - 1]); + + const afterA = obj instanceof a.C; + const afterB = obj instanceof b.C; + + console.log(' before: A=' + beforeA + '(expect true), B=' + beforeB + '(expect false)'); + console.log(' after: A=' + afterA + '(expect false), B=' + afterB + '(expect true)'); + if (afterA !== false || afterB !== true) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof reparented vs B', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (obj instanceof b.C) c++; + return c; + }); + + bench('instanceof reparented vs A', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (obj instanceof a.C) c++; + return c; + }); +} + +// ============================================================ +// Test 3: Splice mid-chain to new root +// ============================================================ +console.log('\n=== Test 3: Splice mid-chain to new root ==='); +{ + const { C, obj, root, chain } = buildChain(24); + const before = obj instanceof C; + + function NewC() {} + const newRoot = { tag: 'new' }; + NewC.prototype = newRoot; + + // detach mid-chain from old root, attach to new root + const midIdx = Math.floor(chain.length / 2); + Object.setPrototypeOf(chain[midIdx], newRoot); + + const afterOld = obj instanceof C; + const afterNew = obj instanceof NewC; + + console.log(' before: instanceof C=' + before + '(expect true)'); + console.log(' after: instanceof C=' + afterOld + '(expect false), instanceof NewC=' + afterNew + '(expect true)'); + if (afterOld !== false || afterNew !== true) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof (post-splice, vs NewC)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (obj instanceof NewC) c++; + return c; + }); + + bench('instanceof (post-splice, vs old C)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if (obj instanceof C) c++; + return c; + }); +} + +// ============================================================ +// Test 4: Constructor.prototype reassignment +// ============================================================ +console.log('\n=== Test 4: Constructor.prototype reassignment ==='); +{ + function Foo() {} + Foo.prototype = { kind: 'original' }; + const obj1 = new Foo(); + const before = obj1 instanceof Foo; + + // swap .prototype to a new object + Foo.prototype = { kind: 'replaced' }; + + const obj1After = obj1 instanceof Foo; // should be FALSE + const obj2 = new Foo(); + const obj2After = obj2 instanceof Foo; // should be TRUE + + console.log(' obj1 before=' + before + '(expect true), obj1 after=' + obj1After + '(expect false), obj2=' + obj2After + '(expect true)'); + if (obj1After !== false || obj2After !== true) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 5: Symbol.hasInstance +// ============================================================ +console.log('\n=== Test 5: Symbol.hasInstance ==='); +{ + function Foo() {} + const obj = new Foo(); + const normalResult = obj instanceof Foo; + const normalFake = {} instanceof Foo; + + Object.defineProperty(Foo, Symbol.hasInstance, { + value: function (instance) { + return true; + }, + configurable: true + }); + + const overrideResult = {} instanceof Foo; + + console.log(' normal: obj=' + normalResult + '(expect true), {}=' + normalFake + '(expect false)'); + console.log(' override: {} instanceof Foo = ' + overrideResult + '(expect true)'); + if (overrideResult !== true) { + console.log(' *** Symbol.hasInstance NOT RESPECTED ***'); + } else { + console.log(' correctness OK'); + } + + bench('instanceof (Symbol.hasInstance always-true)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) if ({} instanceof Foo) c++; + return c; + }); + + // custom logic + let callCount = 0; + Object.defineProperty(Foo, Symbol.hasInstance, { + value: function (inst) { + callCount++; + return inst.special === true; + }, + configurable: true + }); + + const customA = { special: true } instanceof Foo; + const customB = { special: false } instanceof Foo; + console.log(' custom: {special:true}=' + customA + '(expect true), {special:false}=' + customB + '(expect false)'); + console.log(' Symbol.hasInstance was called ' + callCount + ' times'); + if (customA !== true || customB !== false) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 6: Megamorphic — many different shapes +// ============================================================ +console.log('\n=== Test 6: Megamorphic instanceof ==='); +{ + const { C, root, chain } = buildChain(16); + + const objects = []; + for (let i = 0; i < 100; i++) { + const obj = Object.create(chain[chain.length - 1]); + obj['prop_' + i] = i; + obj['extra_' + i * 7] = true; + objects.push(obj); + } + + let idx = 0; + bench('instanceof (100 shapes)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) { + idx = (idx + 1) % 100; + if (objects[idx] instanceof C) c++; + } + return c; + }); +} + +// ============================================================ +// Test 7: Prototype swap every iteration +// ============================================================ +console.log('\n=== Test 7: Prototype swap every iteration ==='); +{ + const { C, root, chain } = buildChain(16); + const midIdx = Math.floor(chain.length / 2); + const originalProto = Object.getPrototypeOf(chain[midIdx]); + + const altRoot = { alt: true }; + let toggle = false; + + bench( + 'instanceof (swap each iter)', + function (n) { + let c = 0; + for (let i = 0; i < n; i++) { + toggle = !toggle; + if (toggle) { + Object.setPrototypeOf(chain[midIdx], altRoot); + } else { + Object.setPrototypeOf(chain[midIdx], originalProto); + } + const obj = Object.create(chain[chain.length - 1]); + if (obj instanceof C) c++; + } + return c; + }, + 100000 + ); +} + +// ============================================================ +// Test 8: Unrelated constructors with same shape +// ============================================================ +console.log('\n=== Test 8: Unrelated constructors with same structure ==='); +{ + function FooA() { + this.x = 1; + } + function FooB() { + this.x = 1; + } + + const objA = new FooA(); + const cross = objA instanceof FooB; + + console.log(' FooA instance instanceof FooB = ' + cross + '(expect false)'); + if (cross !== false) { + console.log(' *** CORRECTNESS BUG ***'); + } else { + console.log(' correctness OK'); + } +} + +// ============================================================ +// Test 9: prop_lookup with shadowing/unshadowing +// ============================================================ +console.log('\n=== Test 9: prop_lookup with prototype mutation ==='); +for (const depth of [8, 24, 40]) { + const { obj, chain } = buildChain(depth); + + console.log('\n--- depth=' + depth + ' ---'); + console.log(' sanity marker=' + obj.marker); + + const midIdx = Math.floor(chain.length / 2); + chain[midIdx].marker = 42; + console.log(' after shadow=' + obj.marker + ' (expected 42)'); + + bench('prop_lookup (shadowed)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) c += obj.marker; + return c; + }); + + delete chain[midIdx].marker; + console.log(' after delete=' + obj.marker + ' (expected 1)'); + + bench('prop_lookup (after delete)', function (n) { + let c = 0; + for (let i = 0; i < n; i++) c += obj.marker; + return c; + }); +} + +console.log('\n=== done ===');