diff --git a/package.json b/package.json index 6fa6e3c6..e5e1a03c 100644 --- a/package.json +++ b/package.json @@ -12,7 +12,7 @@ "scripts": { "format": "prettier --write .", "format:check": "prettier --check .", - "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs", + "postinstall": "node scripts/patch-app-builder-lib.cjs && node scripts/patch-guacamole-lite.cjs && node scripts/patch-better-sqlite3.cjs", "prebuild": "node scripts/write-electron-build-info.cjs", "lint": "eslint .", "lint:fix": "eslint --fix .", diff --git a/scripts/patch-better-sqlite3.cjs b/scripts/patch-better-sqlite3.cjs new file mode 100644 index 00000000..76eb1162 --- /dev/null +++ b/scripts/patch-better-sqlite3.cjs @@ -0,0 +1,97 @@ +const fs = require("node:fs"); +const path = require("node:path"); + +const betterSqlite3Dir = path.join(__dirname, "..", "node_modules", "better-sqlite3"); +const macrosPath = path.join(betterSqlite3Dir, "src", "util", "macros.cpp"); +const helpersPath = path.join(betterSqlite3Dir, "src", "util", "helpers.cpp"); +const entryPath = path.join(betterSqlite3Dir, "src", "better_sqlite3.cpp"); + +function patchFile(filePath, replacements) { + if (!fs.existsSync(filePath)) { + return false; + } + + let source = fs.readFileSync(filePath, "utf8"); + let changed = false; + + for (const { original, patched } of replacements) { + if (source.includes(patched)) { + continue; + } + if (!source.includes(original)) { + continue; + } + source = source.replace(original, patched); + changed = true; + } + + if (changed) { + fs.writeFileSync(filePath, source); + } + + return changed; +} + +if (!fs.existsSync(betterSqlite3Dir)) { + console.log("[patch-better-sqlite3] better-sqlite3 not found, skipping"); + process.exit(0); +} + +const macrosPatched = patchFile(macrosPath, [ + { + original: `#define OnlyContext isolate->GetCurrentContext() +#define OnlyAddon static_cast(info.Data().As()->Value()) +#define UseIsolate v8::Isolate* isolate = OnlyIsolate`, + patched: `#define OnlyContext isolate->GetCurrentContext() +#if defined(V8_MAJOR_VERSION) && V8_MAJOR_VERSION >= 14 +#define BETTER_SQLITE3_EXTERNAL_POINTER_TAG static_cast(0) +#define EXTERNAL_VALUE(external) ((external)->Value(BETTER_SQLITE3_EXTERNAL_POINTER_TAG)) +#define EXTERNAL_NEW(isolate, value) v8::External::New((isolate), (value), BETTER_SQLITE3_EXTERNAL_POINTER_TAG) +#else +#define EXTERNAL_VALUE(external) ((external)->Value()) +#define EXTERNAL_NEW(isolate, value) v8::External::New((isolate), (value)) +#endif +#define OnlyAddon static_cast(EXTERNAL_VALUE(info.Data().As())) +#define UseIsolate v8::Isolate* isolate = OnlyIsolate`, + }, +]); + +const helpersPatched = patchFile(helpersPath, [ + { + original: `\t\trecv->InstanceTemplate()->SetNativeDataProperty( +\t\t\tInternalizedFromLatin1(isolate, name), +\t\t\tfunc, +\t\t\t0, +\t\t\tdata +\t\t);`, + patched: `\t\trecv->InstanceTemplate()->SetNativeDataProperty( +\t\t\tInternalizedFromLatin1(isolate, name), +\t\t\tfunc, +\t\t\tstatic_cast(nullptr), +\t\t\tdata +\t\t);`, + }, +]); + +const entryPatched = patchFile(entryPath, [ + { + original: `#include +#include `, + patched: `#include +#if defined(_MSC_VER) && !defined(__clang__) && !defined(__builtin_frame_address) +#include +#define __builtin_frame_address(level) _AddressOfReturnAddress() +#endif +#include `, + }, + { + original: `\tv8::Local data = v8::External::New(isolate, addon);`, + patched: `\tv8::Local data = EXTERNAL_NEW(isolate, addon);`, + }, +]); + +if (macrosPatched || helpersPatched || entryPatched) { + console.log("[patch-better-sqlite3] Applied compatibility patches for newer Electron/V8"); +} else { + console.log("[patch-better-sqlite3] Already patched or target code not found"); +}