From 1b390ae1a5a95c3e83d9f3d51536ee32a02ab021 Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 8 Jan 2025 17:44:01 -0500 Subject: [PATCH 1/4] Bump Emscripten version to latest --- packages/php-wasm/compile/base-image/Dockerfile | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/php-wasm/compile/base-image/Dockerfile b/packages/php-wasm/compile/base-image/Dockerfile index 9feb3bbcd2..7785f2bcfa 100644 --- a/packages/php-wasm/compile/base-image/Dockerfile +++ b/packages/php-wasm/compile/base-image/Dockerfile @@ -50,8 +50,8 @@ RUN set -euxo pipefail;\ # https://github.com/WordPress/wordpress-playground/tree/67d916b5eccfe78e26e9c953598cc1a81f316931/packages/php-wasm/compile RUN ln -s /usr/bin/python3 /usr/bin/python RUN git clone https://github.com/emscripten-core/emsdk.git && \ - ./emsdk/emsdk install 3.1.61 && \ - /root/emsdk/emsdk activate 3.1.61 + ./emsdk/emsdk install 3.1.74 && \ + /root/emsdk/emsdk activate 3.1.74 RUN mkdir -p /root/lib/lib /root/lib/include /root/lib/share /root/lib/bin From 8f64506d5fb58ab594f5e0e85233aa759b598c5f Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 8 Jan 2025 17:47:05 -0500 Subject: [PATCH 2/4] Upgrade ubuntu image version due to missing older image --- packages/php-wasm/compile/base-image/Dockerfile | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/packages/php-wasm/compile/base-image/Dockerfile b/packages/php-wasm/compile/base-image/Dockerfile index 7785f2bcfa..c6d22b1d08 100644 --- a/packages/php-wasm/compile/base-image/Dockerfile +++ b/packages/php-wasm/compile/base-image/Dockerfile @@ -1,7 +1,6 @@ # Originally forked from https://github.com/seanmorris/php-wasm -# ubuntu:lunar supports amd64 and arm64 (Apple Silicon) while -# emscripten/emsdk:3.1.24 supports amd64 only. -FROM ubuntu:lunar as emscripten +# ubuntu:noble supports amd64 and arm64 (Apple Silicon) +FROM ubuntu:noble as emscripten SHELL ["/bin/bash", "-c"] From 6141015af8128a51bcdb072610185e2fed0bfe1e Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 8 Jan 2025 17:48:49 -0500 Subject: [PATCH 3/4] Fix emscripten output patch for latest version --- packages/php-wasm/compile/php/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/php-wasm/compile/php/Dockerfile b/packages/php-wasm/compile/php/Dockerfile index 0c5bef7215..9abd0b4450 100644 --- a/packages/php-wasm/compile/php/Dockerfile +++ b/packages/php-wasm/compile/php/Dockerfile @@ -1048,7 +1048,7 @@ RUN set -euxo pipefail; \ # This assumes the traffic is always forwarded to the same target. # However, we want to support arbitrary targets, so we need to # replace the hardcoded websocket target URL with a dynamic callback. - /root/replace.sh $'s/if\s*\(\s*["\']string["\']\s*===\s*typeof Module\[["\']websocket["\']\]\[["\']url["\']\]\s*\)/if("function"===typeof Module["websocket"]["url"]) {\nurl = Module["websocket"]["url"](...arguments);\n}else if ("string" === typeof Module["websocket"]["url"])/g' \ + /root/replace.sh $'s/if\s*\(\s*SOCKFS\.websocketArgs\["url"\]\s*\)/if("function"===typeof SOCKFS.websocketArgs["url"]) {\nurl = SOCKFS.websocketArgs["url"](...arguments);\n}else if ("string" === typeof SOCKFS.websocketArgs["url"])/g' \ /root/output/php.js; \ # Enable custom WebSocket constructors to support socket options. /root/replace.sh "s/ws\s*=\s*new WebSocketConstructor/if (Module['websocket']['decorator']) {WebSocketConstructor = Module['websocket']['decorator'](WebSocketConstructor);}ws = new WebSocketConstructor/g" /root/output/php.js && \ From 1eff2421f2cb9b5a44e904fdd0f95469025c2e7d Mon Sep 17 00:00:00 2001 From: Brandon Payton Date: Wed, 8 Jan 2025 17:54:25 -0500 Subject: [PATCH 4/4] WIP --- package-lock.json | 36 +++++++++++++++++++ package.json | 2 ++ packages/php-wasm/compile/php/Dockerfile | 5 +++ .../compile/php/esm-php-exit-status.js | 34 ++++++++++++++++++ packages/php-wasm/compile/php/esm-prefix.js | 35 ------------------ .../compile/php/override-php-exit-status.js | 36 +++++++++++++++++++ 6 files changed, 113 insertions(+), 35 deletions(-) create mode 100644 packages/php-wasm/compile/php/esm-php-exit-status.js create mode 100644 packages/php-wasm/compile/php/override-php-exit-status.js diff --git a/package-lock.json b/package-lock.json index a4814ac14b..e442000f5d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -118,6 +118,7 @@ "eslint-plugin-playground-dev": "file:packages/meta/src/eslint-plugin-playground-dev", "eslint-plugin-react": "7.32.2", "eslint-plugin-react-hooks": "4.6.0", + "estree-toolkit": "1.7.8", "gh-pages": "5.0.0", "glob": "^9.3.0", "husky": "8.0.3", @@ -127,6 +128,7 @@ "jsdom": "22.1.0", "jsonc-eslint-parser": "^2.1.0", "lerna": "6.6.2", + "meriyah": "6.0.3", "nx": "16.9.0", "ora": "6.3.0", "prettier": "^2.6.2", @@ -15804,6 +15806,15 @@ "dev": true, "license": "MIT" }, + "node_modules/@types/estree-jsx": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/@types/estree-jsx/-/estree-jsx-1.0.5.tgz", + "integrity": "sha512-52CcUVNFyfb1A2ALocQw/Dd1BQFNmSdkuC3BkZ6iqhdMfQz7JWOFRuJFloOzjk+6WijU56m9oKXFAXc7o3Towg==", + "dev": true, + "dependencies": { + "@types/estree": "*" + } + }, "node_modules/@types/express": { "version": "4.17.17", "dev": true, @@ -26301,6 +26312,22 @@ "node": ">=4.0" } }, + "node_modules/estree-toolkit": { + "version": "1.7.8", + "resolved": "https://registry.npmjs.org/estree-toolkit/-/estree-toolkit-1.7.8.tgz", + "integrity": "sha512-v0Q0L+0agSDFe3x9Sj7aAzrI9afvsfr5r7AM2SNk/8bKYRQ3tUf4PQEUWe99LkWysmT1PsuSpW+W1w/xZmCKeg==", + "dev": true, + "dependencies": { + "@types/estree": "^1.0.5", + "@types/estree-jsx": "^1.0.5" + } + }, + "node_modules/estree-toolkit/node_modules/@types/estree": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", + "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "dev": true + }, "node_modules/estree-walker": { "version": "2.0.2", "dev": true, @@ -33967,6 +33994,15 @@ "node": ">= 8" } }, + "node_modules/meriyah": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/meriyah/-/meriyah-6.0.3.tgz", + "integrity": "sha512-NqUbuQIjIH8dxUBPTMHS1kwIHd6n6nF3F7oeLXGWqBkpVP2lZxVHdab5JxbFBisIB4axZ9b/lT4HLJfZxmFK7Q==", + "dev": true, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/methods": { "version": "1.1.2", "license": "MIT", diff --git a/package.json b/package.json index 8ec1227d40..ebc4e69edf 100644 --- a/package.json +++ b/package.json @@ -171,6 +171,7 @@ "eslint-plugin-playground-dev": "file:packages/meta/src/eslint-plugin-playground-dev", "eslint-plugin-react": "7.32.2", "eslint-plugin-react-hooks": "4.6.0", + "estree-toolkit": "1.7.8", "gh-pages": "5.0.0", "glob": "^9.3.0", "husky": "8.0.3", @@ -180,6 +181,7 @@ "jsdom": "22.1.0", "jsonc-eslint-parser": "^2.1.0", "lerna": "6.6.2", + "meriyah": "6.0.3", "nx": "16.9.0", "ora": "6.3.0", "prettier": "^2.6.2", diff --git a/packages/php-wasm/compile/php/Dockerfile b/packages/php-wasm/compile/php/Dockerfile index 9abd0b4450..f146ffc689 100644 --- a/packages/php-wasm/compile/php/Dockerfile +++ b/packages/php-wasm/compile/php/Dockerfile @@ -1021,6 +1021,9 @@ RUN set -euxo pipefail; \ # Postprocess the build php.js module: COPY ./php/esm-prefix.js /root/esm-prefix.js COPY ./php/esm-suffix.js /root/esm-suffix.js +COPY ./php/esm-php-exit-status.js /root/esm-php-exit-status.js +RUN apt-get install -y nodejs npm +COPY ./php/override-php-exit-status.js /root/override-php-exit-status.js RUN set -euxo pipefail; \ cp -rfv /build/output/* /root/output/; \ # Figure out the target file names and URLs @@ -1099,6 +1102,8 @@ RUN set -euxo pipefail; \ cat /root/esm-prefix.js >> /root/output/php-module.js && \ cat /root/output/php.js >> /root/output/php-module.js && \ cat /root/esm-suffix.js >> /root/output/php-module.js && \ + npm install estree-toolkit meriyah && \ + node /root/override-php-exit-status.js /root/output/php-module.js && \ \ # Remove the old php.js file rm /root/output/php.js && \ diff --git a/packages/php-wasm/compile/php/esm-php-exit-status.js b/packages/php-wasm/compile/php/esm-php-exit-status.js new file mode 100644 index 0000000000..1620459aea --- /dev/null +++ b/packages/php-wasm/compile/php/esm-php-exit-status.js @@ -0,0 +1,34 @@ +/** + * Overrides Emscripten's default ExitStatus object which gets + * thrown on failure. Unfortunately, the default object is not + * a subclass of Error and does not provide any stack trace. + * + * This is a deliberate behavior on Emscripten's end to prevent + * memory leaks after the program exits. See: + * + * https://github.com/emscripten-core/emscripten/pull/9108 + * + * In case of WordPress Playground, the worker in which the PHP + * runs will typically exit after the PHP program finishes, so + * we don't have to worry about memory leaks. + * + * As for assigning to a previously undeclared ExitStatus variable here, + * the Emscripten module declares `ExitStatus` as `function ExitStatus` + * which means it gets hoisted to the top of the scope and can be + * reassigned here – before the actual declaration is reached. + * + * If that sounds weird, try this example: + * + * ExitStatus = () => { console.log("reassigned"); } + * function ExitStatus() {} + * ExitStatus(); + * // logs "reassigned" + */ +ExitStatus = class PHPExitStatus extends Error { + constructor(status) { + super(status); + this.name = "ExitStatus"; + this.message = "Program terminated with exit(" + status + ")"; + this.status = status; + } +} \ No newline at end of file diff --git a/packages/php-wasm/compile/php/esm-prefix.js b/packages/php-wasm/compile/php/esm-prefix.js index e050dd727c..d23e37c78f 100644 --- a/packages/php-wasm/compile/php/esm-prefix.js +++ b/packages/php-wasm/compile/php/esm-prefix.js @@ -1,37 +1,2 @@ export function init(RuntimeName, PHPLoader) { - /** - * Overrides Emscripten's default ExitStatus object which gets - * thrown on failure. Unfortunately, the default object is not - * a subclass of Error and does not provide any stack trace. - * - * This is a deliberate behavior on Emscripten's end to prevent - * memory leaks after the program exits. See: - * - * https://github.com/emscripten-core/emscripten/pull/9108 - * - * In case of WordPress Playground, the worker in which the PHP - * runs will typically exit after the PHP program finishes, so - * we don't have to worry about memory leaks. - * - * As for assigning to a previously undeclared ExitStatus variable here, - * the Emscripten module declares `ExitStatus` as `function ExitStatus` - * which means it gets hoisted to the top of the scope and can be - * reassigned here – before the actual declaration is reached. - * - * If that sounds weird, try this example: - * - * ExitStatus = () => { console.log("reassigned"); } - * function ExitStatus() {} - * ExitStatus(); - * // logs "reassigned" - */ - ExitStatus = class PHPExitStatus extends Error { - constructor(status) { - super(status); - this.name = "ExitStatus"; - this.message = "Program terminated with exit(" + status + ")"; - this.status = status; - } - } - // The rest of the code comes from the built php.js file and esm-suffix.js diff --git a/packages/php-wasm/compile/php/override-php-exit-status.js b/packages/php-wasm/compile/php/override-php-exit-status.js new file mode 100644 index 0000000000..6941639299 --- /dev/null +++ b/packages/php-wasm/compile/php/override-php-exit-status.js @@ -0,0 +1,36 @@ +import * as path from 'path'; +import * as fs from 'fs'; +import { traverse } from 'estree-toolkit'; +import { parseModule } from 'meriyah'; + +const thisScriptDir = path.dirname(process.argv[1]); + +const phpJsPath = process.argv[2]; +const phpJs = fs.readFileSync(phpJsPath, 'utf8'); +const enhancedExitStatusJsPath = path.resolve( + thisScriptDir, + 'esm-php-exit-status.js' +); +const enhancedExitStatusJs = fs.readFileSync(enhancedExitStatusJsPath, 'utf8'); + +const phpJsAst = parseModule(phpJs, { ranges: true }); +console.log(phpJsAst); + +traverse(phpJsAst, { + ClassDeclaration(path) { + console.log(path.node); + if (path.node?.id?.name === 'ExitStatus') { + const endOfClassDeclaration = path.node.range[1]; + const head = phpJs.substring(0, endOfClassDeclaration); + const tail = phpJs.substring(endOfClassDeclaration); + + const output = `${head}\n${enhancedExitStatusJs}\n${tail}`; + console.log(output); + fs.writeFileSync(phpJsPath, output); + process.exit(0); + } + } +}); + +console.error('Did not find ExitStatus declaration to override.'); +process.exit(-1);