Skip to content

Commit b65965b

Browse files
Implement WASM wrapper improvements to eliminate proto.js dependency
- Add wasm_parse_query_protobuf() and wasm_get_protobuf_len() C functions - Implement protobuf caching using WeakMap in JavaScript - Remove proto.js import from both ES module and CommonJS wrappers - Update Makefile to export new C functions and HEAPU32 runtime method - Remove proto.js from package.json files list - Maintain backward compatibility while eliminating 5.4MB dependency Co-Authored-By: Dan Lynch <[email protected]>
1 parent 82aa3b9 commit b65965b

File tree

5 files changed

+129
-23
lines changed

5 files changed

+129
-23
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,8 @@ ifdef EMSCRIPTEN
5454
$(CXXFLAGS) \
5555
-I$(LIBPG_QUERY_DIR) \
5656
-L$(LIBPG_QUERY_DIR) \
57-
-sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_deparse_protobuf','_wasm_parse_plpgsql','_wasm_fingerprint','_wasm_free_string']" \
58-
-sEXPORTED_RUNTIME_METHODS="['lengthBytesUTF8','stringToUTF8','UTF8ToString','HEAPU8']" \
57+
-sEXPORTED_FUNCTIONS="['_malloc','_free','_wasm_parse_query','_wasm_parse_query_protobuf','_wasm_get_protobuf_len','_wasm_deparse_protobuf','_wasm_parse_plpgsql','_wasm_fingerprint','_wasm_free_string']" \
58+
-sEXPORTED_RUNTIME_METHODS="['lengthBytesUTF8','stringToUTF8','UTF8ToString','HEAPU8','HEAPU32']" \
5959
-sEXPORT_NAME="$(WASM_MODULE_NAME)" \
6060
-sENVIRONMENT="web,node" \
6161
-sMODULARIZE=1 \

package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@
1111
"files": [
1212
"index.js",
1313
"index.d.ts",
14-
"proto.js",
1514
"libpg_query/*",
1615
"script/*",
1716
"wasm/*"

src/wasm_wrapper.c

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,39 @@ char* wasm_fingerprint(const char* input) {
6868
return fingerprint_str;
6969
}
7070

71+
EMSCRIPTEN_KEEPALIVE
72+
char* wasm_parse_query_protobuf(const char* input, int* out_len) {
73+
PgQueryProtobufParseResult result = pg_query_parse_protobuf(input);
74+
75+
if (result.error) {
76+
*out_len = 0;
77+
char* error_msg = strdup(result.error->message);
78+
pg_query_free_protobuf_parse_result(result);
79+
return error_msg;
80+
}
81+
82+
char* protobuf_data = malloc(result.parse_tree.len);
83+
memcpy(protobuf_data, result.parse_tree.data, result.parse_tree.len);
84+
*out_len = (int)result.parse_tree.len;
85+
86+
pg_query_free_protobuf_parse_result(result);
87+
return protobuf_data;
88+
}
89+
90+
EMSCRIPTEN_KEEPALIVE
91+
int wasm_get_protobuf_len(const char* input) {
92+
PgQueryProtobufParseResult result = pg_query_parse_protobuf(input);
93+
94+
if (result.error) {
95+
pg_query_free_protobuf_parse_result(result);
96+
return 0;
97+
}
98+
99+
int len = (int)result.parse_tree.len;
100+
pg_query_free_protobuf_parse_result(result);
101+
return len;
102+
}
103+
71104
EMSCRIPTEN_KEEPALIVE
72105
void wasm_free_string(char* str) {
73106
free(str);

wasm/index.cjs

Lines changed: 62 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
const { pg_query } = require('../proto.js');
21
const PgQueryModule = require('./libpg-query.js');
32

43
let wasmModule;
@@ -30,9 +29,12 @@ function ptrToString(ptr) {
3029
return wasmModule.UTF8ToString(ptr);
3130
}
3231

32+
const protobufCache = new WeakMap();
33+
3334
const parseQuery = awaitInit(async (query) => {
3435
const queryPtr = stringToPtr(query);
3536
let resultPtr;
37+
let protobufPtr;
3638

3739
try {
3840
resultPtr = wasmModule._wasm_parse_query(queryPtr);
@@ -42,25 +44,48 @@ const parseQuery = awaitInit(async (query) => {
4244
throw new Error(resultStr);
4345
}
4446

45-
return JSON.parse(resultStr);
47+
const parseResult = JSON.parse(resultStr);
48+
49+
const protobufLen = wasmModule._wasm_get_protobuf_len(queryPtr);
50+
if (protobufLen > 0) {
51+
const lenPtr = wasmModule._malloc(4);
52+
wasmModule.HEAPU32[lenPtr >> 2] = 0;
53+
protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lenPtr);
54+
const actualLen = wasmModule.HEAPU32[lenPtr >> 2];
55+
wasmModule._free(lenPtr);
56+
57+
if (actualLen > 0) {
58+
const protobufData = new Uint8Array(wasmModule.HEAPU8.buffer, protobufPtr, actualLen);
59+
const protobufCopy = new Uint8Array(protobufData);
60+
protobufCache.set(parseResult, protobufCopy);
61+
}
62+
}
63+
64+
return parseResult;
4665
} finally {
4766
wasmModule._free(queryPtr);
4867
if (resultPtr) {
4968
wasmModule._wasm_free_string(resultPtr);
5069
}
70+
if (protobufPtr) {
71+
wasmModule._wasm_free_string(protobufPtr);
72+
}
5173
}
5274
});
5375

5476
const deparse = awaitInit(async (parseTree) => {
55-
const msg = pg_query.ParseResult.fromObject(parseTree);
56-
const data = pg_query.ParseResult.encode(msg).finish();
77+
const protobufData = protobufCache.get(parseTree);
78+
79+
if (!protobufData) {
80+
throw new Error('No protobuf data found for parse tree. Make sure to use the result from parseQuery directly.');
81+
}
5782

58-
const dataPtr = wasmModule._malloc(data.length);
83+
const dataPtr = wasmModule._malloc(protobufData.length);
5984
let resultPtr;
6085

6186
try {
62-
wasmModule.HEAPU8.set(data, dataPtr);
63-
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
87+
wasmModule.HEAPU8.set(protobufData, dataPtr);
88+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, protobufData.length);
6489
const resultStr = ptrToString(resultPtr);
6590

6691
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {
@@ -125,6 +150,7 @@ function parseQuerySync(query) {
125150
}
126151
const queryPtr = stringToPtr(query);
127152
let resultPtr;
153+
let protobufPtr;
128154

129155
try {
130156
resultPtr = wasmModule._wasm_parse_query(queryPtr);
@@ -134,28 +160,51 @@ function parseQuerySync(query) {
134160
throw new Error(resultStr);
135161
}
136162

137-
return JSON.parse(resultStr);
163+
const parseResult = JSON.parse(resultStr);
164+
165+
const protobufLen = wasmModule._wasm_get_protobuf_len(queryPtr);
166+
if (protobufLen > 0) {
167+
const lenPtr = wasmModule._malloc(4);
168+
wasmModule.HEAPU32[lenPtr >> 2] = 0;
169+
protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lenPtr);
170+
const actualLen = wasmModule.HEAPU32[lenPtr >> 2];
171+
wasmModule._free(lenPtr);
172+
173+
if (actualLen > 0) {
174+
const protobufData = new Uint8Array(wasmModule.HEAPU8.buffer, protobufPtr, actualLen);
175+
const protobufCopy = new Uint8Array(protobufData);
176+
protobufCache.set(parseResult, protobufCopy);
177+
}
178+
}
179+
180+
return parseResult;
138181
} finally {
139182
wasmModule._free(queryPtr);
140183
if (resultPtr) {
141184
wasmModule._wasm_free_string(resultPtr);
142185
}
186+
if (protobufPtr) {
187+
wasmModule._wasm_free_string(protobufPtr);
188+
}
143189
}
144190
}
145191

146192
function deparseSync(parseTree) {
147193
if (!wasmModule) {
148194
throw new Error('WASM module not initialized. Call an async method first to initialize.');
149195
}
150-
const msg = pg_query.ParseResult.fromObject(parseTree);
151-
const data = pg_query.ParseResult.encode(msg).finish();
196+
const protobufData = protobufCache.get(parseTree);
197+
198+
if (!protobufData) {
199+
throw new Error('No protobuf data found for parse tree. Make sure to use the result from parseQuery directly.');
200+
}
152201

153-
const dataPtr = wasmModule._malloc(data.length);
202+
const dataPtr = wasmModule._malloc(protobufData.length);
154203
let resultPtr;
155204

156205
try {
157-
wasmModule.HEAPU8.set(data, dataPtr);
158-
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
206+
wasmModule.HEAPU8.set(protobufData, dataPtr);
207+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, protobufData.length);
159208
const resultStr = ptrToString(resultPtr);
160209

161210
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {

wasm/index.js

Lines changed: 32 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,3 @@
1-
import { pg_query } from '../proto.js';
21
import PgQueryModule from './libpg-query.js';
32

43
let wasmModule;
@@ -30,9 +29,12 @@ function ptrToString(ptr) {
3029
return wasmModule.UTF8ToString(ptr);
3130
}
3231

32+
const protobufCache = new WeakMap();
33+
3334
export const parseQuery = awaitInit(async (query) => {
3435
const queryPtr = stringToPtr(query);
3536
let resultPtr;
37+
let protobufPtr;
3638

3739
try {
3840
resultPtr = wasmModule._wasm_parse_query(queryPtr);
@@ -42,25 +44,48 @@ export const parseQuery = awaitInit(async (query) => {
4244
throw new Error(resultStr);
4345
}
4446

45-
return JSON.parse(resultStr);
47+
const parseResult = JSON.parse(resultStr);
48+
49+
const protobufLen = wasmModule._wasm_get_protobuf_len(queryPtr);
50+
if (protobufLen > 0) {
51+
const lenPtr = wasmModule._malloc(4);
52+
wasmModule.HEAPU32[lenPtr >> 2] = 0;
53+
protobufPtr = wasmModule._wasm_parse_query_protobuf(queryPtr, lenPtr);
54+
const actualLen = wasmModule.HEAPU32[lenPtr >> 2];
55+
wasmModule._free(lenPtr);
56+
57+
if (actualLen > 0) {
58+
const protobufData = new Uint8Array(wasmModule.HEAPU8.buffer, protobufPtr, actualLen);
59+
const protobufCopy = new Uint8Array(protobufData);
60+
protobufCache.set(parseResult, protobufCopy);
61+
}
62+
}
63+
64+
return parseResult;
4665
} finally {
4766
wasmModule._free(queryPtr);
4867
if (resultPtr) {
4968
wasmModule._wasm_free_string(resultPtr);
5069
}
70+
if (protobufPtr) {
71+
wasmModule._wasm_free_string(protobufPtr);
72+
}
5173
}
5274
});
5375

5476
export const deparse = awaitInit(async (parseTree) => {
55-
const msg = pg_query.ParseResult.fromObject(parseTree);
56-
const data = pg_query.ParseResult.encode(msg).finish();
77+
const protobufData = protobufCache.get(parseTree);
78+
79+
if (!protobufData) {
80+
throw new Error('No protobuf data found for parse tree. Make sure to use the result from parseQuery directly.');
81+
}
5782

58-
const dataPtr = wasmModule._malloc(data.length);
83+
const dataPtr = wasmModule._malloc(protobufData.length);
5984
let resultPtr;
6085

6186
try {
62-
wasmModule.HEAPU8.set(data, dataPtr);
63-
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, data.length);
87+
wasmModule.HEAPU8.set(protobufData, dataPtr);
88+
resultPtr = wasmModule._wasm_deparse_protobuf(dataPtr, protobufData.length);
6489
const resultStr = ptrToString(resultPtr);
6590

6691
if (resultStr.startsWith('syntax error') || resultStr.startsWith('deparse error') || resultStr.includes('ERROR')) {

0 commit comments

Comments
 (0)