From ea12beef7a74b5da92adb4d883bd01e3fd33a9c1 Mon Sep 17 00:00:00 2001 From: Anna Henningsen Date: Wed, 14 Aug 2024 19:05:31 +0200 Subject: [PATCH] fix(cli-repl): patch WASM wrapper for pac-proxy-agent on s390x MONGOSH-1858 (#2130) Work around https://github.com/justjake/quickjs-emscripten/issues/123. --- packages/cli-repl/.depcheckrc | 2 + .../cli-repl/src/pac-proxy-s390x-patch.ts | 53 +++++++++++++++++++ packages/cli-repl/src/run.ts | 3 ++ packages/e2e-tests/test/e2e-snapshot.spec.ts | 26 ++++++--- 4 files changed, 77 insertions(+), 7 deletions(-) create mode 100644 packages/cli-repl/src/pac-proxy-s390x-patch.ts diff --git a/packages/cli-repl/.depcheckrc b/packages/cli-repl/.depcheckrc index e3286fbd1..ba9a7904d 100644 --- a/packages/cli-repl/.depcheckrc +++ b/packages/cli-repl/.depcheckrc @@ -18,5 +18,7 @@ ignores: - ipv6-normalize - bindings - system-ca + # used for monkey-patching our s390x fix in + - '@tootallnate/quickjs-emscripten' ignore-patterns: - .eslintrc.js diff --git a/packages/cli-repl/src/pac-proxy-s390x-patch.ts b/packages/cli-repl/src/pac-proxy-s390x-patch.ts new file mode 100644 index 000000000..408ac1e5a --- /dev/null +++ b/packages/cli-repl/src/pac-proxy-s390x-patch.ts @@ -0,0 +1,53 @@ +import assert from 'assert'; + +const isBigEndian = + new Int32Array(new Uint8Array([1, 0, 0, 0]).buffer)[0] !== 1; + +// The pac-proxy-agent module uses a WebAssembly agent under the hood for +// safely evaluating JS. The interface for this doesn't properly account for +// little-endian vs. big-endian host platform distinctions. + +// Official support for s390x may not be planned: https://github.com/justjake/quickjs-emscripten/issues/123 + +export let applyPacProxyS390XPatch: () => void; +if (isBigEndian) { + applyPacProxyS390XPatch = () => { + // eslint-disable-next-line @typescript-eslint/no-var-requires + const { + ModuleMemory, + } = require('@tootallnate/quickjs-emscripten/dist/memory'); + + const { toPointerArray } = ModuleMemory.prototype; + { + // Consistency check: The version we are currently using is actually broken + const HEAPU8 = new Uint8Array(4); + toPointerArray.call( + new ModuleMemory({ + _malloc: () => 0, + HEAPU8, + }), + [{ value: 0x01020304 }] + ); + assert.deepStrictEqual([...HEAPU8], [1, 2, 3, 4]); // should be 4, 3, 2, 1 + } + + ModuleMemory.prototype.toPointerArray = function ( + handleArray: { value: number }[] + ) { + return toPointerArray.call( + this, + handleArray.map(({ value }) => ({ + value: + ((value & 0x000000ff) << 24) | + ((value & 0x0000ff00) << 8) | + ((value & 0x00ff0000) >> 8) | + ((value & 0xff000000) >> 24), + })) + ); + }; + }; +} else { + applyPacProxyS390XPatch = () => { + // no-op + }; +} diff --git a/packages/cli-repl/src/run.ts b/packages/cli-repl/src/run.ts index de9035ae0..e826f4900 100644 --- a/packages/cli-repl/src/run.ts +++ b/packages/cli-repl/src/run.ts @@ -22,6 +22,7 @@ import { baseBuildInfo, buildInfo } from './build-info'; import { getStoragePaths, getGlobalConfigPaths } from './config-directory'; import { getCryptLibraryPaths } from './crypt-library-paths'; import { getTlsCertificateSelector } from './tls-certificate-selector'; +import { applyPacProxyS390XPatch } from './pac-proxy-s390x-patch'; import { redactURICredentials } from '@mongosh/history'; import { generateConnectionInfoFromCliArgs } from '@mongosh/arg-parser'; import askcharacter from 'askcharacter'; @@ -170,6 +171,8 @@ async function main() { process.removeAllListeners('SIGINT'); } + applyPacProxyS390XPatch(); + // If we are spawned via Windows doubleclick, ask the user for an URI to // connect to. Allow an environment variable to override this for testing. isSingleConsoleProcess = diff --git a/packages/e2e-tests/test/e2e-snapshot.spec.ts b/packages/e2e-tests/test/e2e-snapshot.spec.ts index 8ba76403b..e4caa28fc 100644 --- a/packages/e2e-tests/test/e2e-snapshot.spec.ts +++ b/packages/e2e-tests/test/e2e-snapshot.spec.ts @@ -15,7 +15,7 @@ const commonPrefix = (a: string, b: string): string => ? a : b && commonPrefix(a, b.slice(0, -1)); -describe('e2e startup banners', function () { +describe('e2e snapshot support', function () { skipIfApiStrict(); afterEach(TestShell.cleanup); @@ -103,14 +103,22 @@ describe('e2e startup banners', function () { // console.table(categorized.map(([m, c]) => [m.replace(prefix, ''), c])); const verifyAllInCategoryMatch = ( category: (typeof categorized)[number][1], - re: RegExp + re: RegExp, + negative = false ) => { for (const [module, cat] of categorized) { if (cat === category) { - expect(module).to.match( - re, - `Found unexpected '${module}' in category '${cat}'` - ); + if (negative) { + expect(module).not.to.match( + re, + `Found unexpected '${module}' in category '${cat}'` + ); + } else { + expect(module).to.match( + re, + `Found unexpected '${module}' in category '${cat}'` + ); + } } } }; @@ -138,8 +146,12 @@ describe('e2e startup banners', function () { ); verifyAllInCategoryMatch( 'nodb-eval', - /^node_modules\/(kerberos|mongodb-client-encryption|glibc-version|@mongodb-js\/devtools-proxy-support|@mongodb-js\/socksv5|agent-base|(win|macos)-export-certificate-and-key)\// + /^node_modules\/(kerberos|mongodb-client-encryption|glibc-version|@mongodb-js\/devtools-proxy-support|@mongodb-js\/socksv5|agent-base|(win|macos)-export-certificate-and-key|@tootallnate\/quickjs-emscripten)\// ); + if (process.arch !== 's390x') { + // quickjs is in the list above but should be exlucded anywhere but on s390x + verifyAllInCategoryMatch('nodb-eval', /quickjs-emscripten/, true); + } verifyAllThatMatchAreInCategory( 'not-loaded', /^node_modules\/(express|openid-client|qs|send|jose|execa|body-parser|@babel\/highlight|@babel\/code-frame)\//