Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions full/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
2 changes: 1 addition & 1 deletion full/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
"wasm:rebuild": "pnpm wasm:make rebuild",
"wasm:clean": "pnpm wasm:make clean",
"wasm:clean-cache": "pnpm wasm:make clean-cache",
"test": "node --test test/parsing.test.js test/fingerprint.test.js test/normalize.test.js test/plpgsql.test.js test/scan.test.js test/errors.test.js",
"test": "node --test test/parsing.test.js test/fingerprint.test.js test/normalize.test.js test/plpgsql.test.js test/scan.test.js test/errors.test.js test/memory.test.js test/deep-nesting.test.js",
"yamlize": "node ./scripts/yamlize.js"
},
"author": "Constructive <developers@constructive.io>",
Expand Down
29 changes: 29 additions & 0 deletions full/test/deep-nesting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const query = require('../');

// Pairs with the reduced STACK_SIZE in the Makefile. Parsing and serializing a
// deeply nested tree recurses on the WASM C stack; if that stack is too small,
// deep input crashes the module with a hard WebAssembly trap instead of failing
// gracefully. This test asserts the parser always either succeeds or throws a
// clean, catchable error (e.g. SqlError "memory exhausted") — never a WASM trap.
// CI rebuilds the WASM from source, so a stack that's too small fails here.
describe('Deep nesting fails gracefully', () => {
// Nested function calls build a genuinely deep parse tree (FuncCall nodes) —
// the shape that recurses hardest during tree->JSON serialization. Depths span
// the band that parses today through where it fails cleanly.
const nestedFuncs = (depth) => 'SELECT ' + 'f('.repeat(depth) + '1' + ')'.repeat(depth);

for (const depth of [1000, 4000, 7000, 10000]) {
it(`depth ${depth}: parses or fails cleanly, never a WASM trap`, async () => {
try {
await query.parse(nestedFuncs(depth));
} catch (err) {
assert.ok(
!(err instanceof WebAssembly.RuntimeError),
`deep input crashed with a WASM trap (stack too small?): ${err && err.message}`
);
}
});
}
});
29 changes: 29 additions & 0 deletions full/test/memory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

// Emscripten MODULARIZE factory; calling it instantiates a fresh, isolated
// WASM instance with its own linear memory.
const createModule = require('../wasm/libpg-query.js');

// Guards the WASM load-time footprint from regressing. The module reserves its
// initial linear memory on instantiation, before any parse — that reservation
// is what inflates load-time memory. Growth is enabled (-sALLOW_MEMORY_GROWTH),
// so the initial size is a footprint knob, not a hard limit. We read the live
// size from the instance (HEAPU8 views the linear memory) instead of parsing
// the binary: no dependency, and it reflects the real allocation.
describe('WASM memory footprint', () => {
// Ceiling for the initial reservation; the single source of truth. Keep it a
// bit above -sINITIAL_MEMORY in the Makefile so normal tuning doesn't trip it.
const MAX_INITIAL_BYTES = 64 * 1024 * 1024;

it('reserves an initial linear memory within budget', async () => {
const m = await createModule();
const initialBytes = m.HEAPU8.buffer.byteLength;
const mib = (n) => (n / (1024 * 1024)).toFixed(0);
assert.ok(
initialBytes <= MAX_INITIAL_BYTES,
`WASM initial memory ${mib(initialBytes)} MiB exceeds the ${mib(MAX_INITIAL_BYTES)} MiB budget. ` +
`Lower -sINITIAL_MEMORY in the Makefile (memory grows on demand via -sALLOW_MEMORY_GROWTH).`
);
});
});
4 changes: 2 additions & 2 deletions templates/Makefile.template
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
4 changes: 2 additions & 2 deletions versions/13/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -74,9 +74,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
4 changes: 2 additions & 2 deletions versions/14/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
2 changes: 1 addition & 1 deletion versions/14/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"wasm:rebuild": "pnpm wasm:make rebuild",
"wasm:clean": "pnpm wasm:make clean",
"wasm:clean-cache": "pnpm wasm:make clean-cache",
"test": "node --test test/parsing.test.js test/errors.test.js"
"test": "node --test test/parsing.test.js test/errors.test.js test/memory.test.js test/deep-nesting.test.js"
},
"author": "Constructive <developers@constructive.io>",
"license": "MIT",
Expand Down
29 changes: 29 additions & 0 deletions versions/14/test/deep-nesting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const query = require('../');

// Pairs with the reduced STACK_SIZE in the Makefile. Parsing and serializing a
// deeply nested tree recurses on the WASM C stack; if that stack is too small,
// deep input crashes the module with a hard WebAssembly trap instead of failing
// gracefully. This test asserts the parser always either succeeds or throws a
// clean, catchable error (e.g. SqlError "memory exhausted") — never a WASM trap.
// CI rebuilds the WASM from source, so a stack that's too small fails here.
describe('Deep nesting fails gracefully', () => {
// Nested function calls build a genuinely deep parse tree (FuncCall nodes) —
// the shape that recurses hardest during tree->JSON serialization. Depths span
// the band that parses today through where it fails cleanly.
const nestedFuncs = (depth) => 'SELECT ' + 'f('.repeat(depth) + '1' + ')'.repeat(depth);

for (const depth of [1000, 4000, 7000, 10000]) {
it(`depth ${depth}: parses or fails cleanly, never a WASM trap`, async () => {
try {
await query.parse(nestedFuncs(depth));
} catch (err) {
assert.ok(
!(err instanceof WebAssembly.RuntimeError),
`deep input crashed with a WASM trap (stack too small?): ${err && err.message}`
);
}
});
}
});
29 changes: 29 additions & 0 deletions versions/14/test/memory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

// Emscripten MODULARIZE factory; calling it instantiates a fresh, isolated
// WASM instance with its own linear memory.
const createModule = require('../wasm/libpg-query.js');

// Guards the WASM load-time footprint from regressing. The module reserves its
// initial linear memory on instantiation, before any parse — that reservation
// is what inflates load-time memory. Growth is enabled (-sALLOW_MEMORY_GROWTH),
// so the initial size is a footprint knob, not a hard limit. We read the live
// size from the instance (HEAPU8 views the linear memory) instead of parsing
// the binary: no dependency, and it reflects the real allocation.
describe('WASM memory footprint', () => {
// Ceiling for the initial reservation; the single source of truth. Keep it a
// bit above -sINITIAL_MEMORY in the Makefile so normal tuning doesn't trip it.
const MAX_INITIAL_BYTES = 64 * 1024 * 1024;

it('reserves an initial linear memory within budget', async () => {
const m = await createModule();
const initialBytes = m.HEAPU8.buffer.byteLength;
const mib = (n) => (n / (1024 * 1024)).toFixed(0);
assert.ok(
initialBytes <= MAX_INITIAL_BYTES,
`WASM initial memory ${mib(initialBytes)} MiB exceeds the ${mib(MAX_INITIAL_BYTES)} MiB budget. ` +
`Lower -sINITIAL_MEMORY in the Makefile (memory grows on demand via -sALLOW_MEMORY_GROWTH).`
);
});
});
4 changes: 2 additions & 2 deletions versions/15/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
2 changes: 1 addition & 1 deletion versions/15/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"wasm:rebuild": "pnpm wasm:make rebuild",
"wasm:clean": "pnpm wasm:make clean",
"wasm:clean-cache": "pnpm wasm:make clean-cache",
"test": "node --test test/parsing.test.js test/errors.test.js"
"test": "node --test test/parsing.test.js test/errors.test.js test/memory.test.js test/deep-nesting.test.js"
},
"author": "Constructive <developers@constructive.io>",
"license": "MIT",
Expand Down
29 changes: 29 additions & 0 deletions versions/15/test/deep-nesting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const query = require('../');

// Pairs with the reduced STACK_SIZE in the Makefile. Parsing and serializing a
// deeply nested tree recurses on the WASM C stack; if that stack is too small,
// deep input crashes the module with a hard WebAssembly trap instead of failing
// gracefully. This test asserts the parser always either succeeds or throws a
// clean, catchable error (e.g. SqlError "memory exhausted") — never a WASM trap.
// CI rebuilds the WASM from source, so a stack that's too small fails here.
describe('Deep nesting fails gracefully', () => {
// Nested function calls build a genuinely deep parse tree (FuncCall nodes) —
// the shape that recurses hardest during tree->JSON serialization. Depths span
// the band that parses today through where it fails cleanly.
const nestedFuncs = (depth) => 'SELECT ' + 'f('.repeat(depth) + '1' + ')'.repeat(depth);

for (const depth of [1000, 4000, 7000, 10000]) {
it(`depth ${depth}: parses or fails cleanly, never a WASM trap`, async () => {
try {
await query.parse(nestedFuncs(depth));
} catch (err) {
assert.ok(
!(err instanceof WebAssembly.RuntimeError),
`deep input crashed with a WASM trap (stack too small?): ${err && err.message}`
);
}
});
}
});
29 changes: 29 additions & 0 deletions versions/15/test/memory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

// Emscripten MODULARIZE factory; calling it instantiates a fresh, isolated
// WASM instance with its own linear memory.
const createModule = require('../wasm/libpg-query.js');

// Guards the WASM load-time footprint from regressing. The module reserves its
// initial linear memory on instantiation, before any parse — that reservation
// is what inflates load-time memory. Growth is enabled (-sALLOW_MEMORY_GROWTH),
// so the initial size is a footprint knob, not a hard limit. We read the live
// size from the instance (HEAPU8 views the linear memory) instead of parsing
// the binary: no dependency, and it reflects the real allocation.
describe('WASM memory footprint', () => {
// Ceiling for the initial reservation; the single source of truth. Keep it a
// bit above -sINITIAL_MEMORY in the Makefile so normal tuning doesn't trip it.
const MAX_INITIAL_BYTES = 64 * 1024 * 1024;

it('reserves an initial linear memory within budget', async () => {
const m = await createModule();
const initialBytes = m.HEAPU8.buffer.byteLength;
const mib = (n) => (n / (1024 * 1024)).toFixed(0);
assert.ok(
initialBytes <= MAX_INITIAL_BYTES,
`WASM initial memory ${mib(initialBytes)} MiB exceeds the ${mib(MAX_INITIAL_BYTES)} MiB budget. ` +
`Lower -sINITIAL_MEMORY in the Makefile (memory grows on demand via -sALLOW_MEMORY_GROWTH).`
);
});
});
4 changes: 2 additions & 2 deletions versions/16/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
2 changes: 1 addition & 1 deletion versions/16/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"wasm:rebuild": "pnpm wasm:make rebuild",
"wasm:clean": "pnpm wasm:make clean",
"wasm:clean-cache": "pnpm wasm:make clean-cache",
"test": "node --test test/parsing.test.js test/errors.test.js"
"test": "node --test test/parsing.test.js test/errors.test.js test/memory.test.js test/deep-nesting.test.js"
},
"author": "Constructive <developers@constructive.io>",
"license": "MIT",
Expand Down
29 changes: 29 additions & 0 deletions versions/16/test/deep-nesting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const query = require('../');

// Pairs with the reduced STACK_SIZE in the Makefile. Parsing and serializing a
// deeply nested tree recurses on the WASM C stack; if that stack is too small,
// deep input crashes the module with a hard WebAssembly trap instead of failing
// gracefully. This test asserts the parser always either succeeds or throws a
// clean, catchable error (e.g. SqlError "memory exhausted") — never a WASM trap.
// CI rebuilds the WASM from source, so a stack that's too small fails here.
describe('Deep nesting fails gracefully', () => {
// Nested function calls build a genuinely deep parse tree (FuncCall nodes) —
// the shape that recurses hardest during tree->JSON serialization. Depths span
// the band that parses today through where it fails cleanly.
const nestedFuncs = (depth) => 'SELECT ' + 'f('.repeat(depth) + '1' + ')'.repeat(depth);

for (const depth of [1000, 4000, 7000, 10000]) {
it(`depth ${depth}: parses or fails cleanly, never a WASM trap`, async () => {
try {
await query.parse(nestedFuncs(depth));
} catch (err) {
assert.ok(
!(err instanceof WebAssembly.RuntimeError),
`deep input crashed with a WASM trap (stack too small?): ${err && err.message}`
);
}
});
}
});
29 changes: 29 additions & 0 deletions versions/16/test/memory.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');

// Emscripten MODULARIZE factory; calling it instantiates a fresh, isolated
// WASM instance with its own linear memory.
const createModule = require('../wasm/libpg-query.js');

// Guards the WASM load-time footprint from regressing. The module reserves its
// initial linear memory on instantiation, before any parse — that reservation
// is what inflates load-time memory. Growth is enabled (-sALLOW_MEMORY_GROWTH),
// so the initial size is a footprint knob, not a hard limit. We read the live
// size from the instance (HEAPU8 views the linear memory) instead of parsing
// the binary: no dependency, and it reflects the real allocation.
describe('WASM memory footprint', () => {
// Ceiling for the initial reservation; the single source of truth. Keep it a
// bit above -sINITIAL_MEMORY in the Makefile so normal tuning doesn't trip it.
const MAX_INITIAL_BYTES = 64 * 1024 * 1024;

it('reserves an initial linear memory within budget', async () => {
const m = await createModule();
const initialBytes = m.HEAPU8.buffer.byteLength;
const mib = (n) => (n / (1024 * 1024)).toFixed(0);
assert.ok(
initialBytes <= MAX_INITIAL_BYTES,
`WASM initial memory ${mib(initialBytes)} MiB exceeds the ${mib(MAX_INITIAL_BYTES)} MiB budget. ` +
`Lower -sINITIAL_MEMORY in the Makefile (memory grows on demand via -sALLOW_MEMORY_GROWTH).`
);
});
});
4 changes: 2 additions & 2 deletions versions/17/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -71,9 +71,9 @@ ifdef EMSCRIPTEN
-sMODULARIZE=1 \
-sEXPORT_ES6=0 \
-sALLOW_MEMORY_GROWTH=1 \
-sINITIAL_MEMORY=134217728 \
-sINITIAL_MEMORY=25165824 \
-sMAXIMUM_MEMORY=1073741824 \
-sSTACK_SIZE=33554432 \
-sSTACK_SIZE=16777216 \
-lpg_query \
-o $@ \
$(SRC_FILES)
Expand Down
2 changes: 1 addition & 1 deletion versions/17/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"wasm:rebuild": "pnpm wasm:make rebuild",
"wasm:clean": "pnpm wasm:make clean",
"wasm:clean-cache": "pnpm wasm:make clean-cache",
"test": "node --test test/parsing.test.js test/errors.test.js"
"test": "node --test test/parsing.test.js test/errors.test.js test/memory.test.js test/deep-nesting.test.js"
},
"author": "Constructive <developers@constructive.io>",
"license": "MIT",
Expand Down
29 changes: 29 additions & 0 deletions versions/17/test/deep-nesting.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
const { describe, it } = require('node:test');
const assert = require('node:assert/strict');
const query = require('../');

// Pairs with the reduced STACK_SIZE in the Makefile. Parsing and serializing a
// deeply nested tree recurses on the WASM C stack; if that stack is too small,
// deep input crashes the module with a hard WebAssembly trap instead of failing
// gracefully. This test asserts the parser always either succeeds or throws a
// clean, catchable error (e.g. SqlError "memory exhausted") — never a WASM trap.
// CI rebuilds the WASM from source, so a stack that's too small fails here.
describe('Deep nesting fails gracefully', () => {
// Nested function calls build a genuinely deep parse tree (FuncCall nodes) —
// the shape that recurses hardest during tree->JSON serialization. Depths span
// the band that parses today through where it fails cleanly.
const nestedFuncs = (depth) => 'SELECT ' + 'f('.repeat(depth) + '1' + ')'.repeat(depth);

for (const depth of [1000, 4000, 7000, 10000]) {
it(`depth ${depth}: parses or fails cleanly, never a WASM trap`, async () => {
try {
await query.parse(nestedFuncs(depth));
} catch (err) {
assert.ok(
!(err instanceof WebAssembly.RuntimeError),
`deep input crashed with a WASM trap (stack too small?): ${err && err.message}`
);
}
});
}
});
Loading