Skip to content

Commit

Permalink
PHP: Ensures that ExitStatus error thrown my Emscripten has a stack t…
Browse files Browse the repository at this point in the history
…race attached (#470)

## Description

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:

emscripten-core/emscripten#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:

```js
ExitStatus = () => { console.log("reassigned"); }
function ExitStatus() {}
ExitStatus();
// logs "reassigned"
```

## Testing instructions

Confirm the CI tests passed

Related: #416
cc @wojtekn
  • Loading branch information
adamziel authored May 30, 2023
1 parent c78999b commit 01357b9
Show file tree
Hide file tree
Showing 39 changed files with 912 additions and 77 deletions.
11 changes: 6 additions & 5 deletions packages/php-wasm/compile/Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -973,7 +973,8 @@ RUN ls /root/output/
# fi

# Postprocess the build php.js module:
COPY ./build-assets/append-before-return.js /root/append-before-return.js
COPY ./build-assets/esm-prefix.js /root/esm-prefix.js
COPY ./build-assets/esm-suffix.js /root/esm-suffix.js
RUN \
# Figure out the target file names and URLs
# The .js and .wasm filenames should reflect the build configuration, e.g.:
Expand Down Expand Up @@ -1039,16 +1040,16 @@ RUN \
# Turn the php.js file into an ES module
# Manually turn the output into a esm module instead of relying on -s MODULARIZE=1.
# which pollutes the global namespace and does not play well with import() mechanics.
echo "export const dependenciesTotalSize = $FILE_SIZE; " >> /root/output/php-module.js && \
if [ "$EMSCRIPTEN_ENVIRONMENT" = "node" ]; then \
echo "const dependencyFilename = __dirname + '/$WASM_FILENAME'; " >> /root/output/php-module.js; \
else \
echo "import dependencyFilename from './$WASM_FILENAME'; " >> /root/output/php-module.js; \
fi; \
echo " export { dependencyFilename }; export function init(RuntimeName, PHPLoader) {" >> /root/output/php-module.js && \
echo " export { dependencyFilename }; " >> /root/output/php-module.js && \
echo "export const dependenciesTotalSize = $FILE_SIZE; " >> /root/output/php-module.js && \
cat /root/esm-prefix.js >> /root/output/php-module.js && \
cat /root/output/php.js >> /root/output/php-module.js && \
cat /root/append-before-return.js >> /root/output/php-module.js && \
echo " return PHPLoader; }" >> /root/output/php-module.js && \
cat /root/esm-suffix.js >> /root/output/php-module.js && \
\
# Remove the old php.js file
rm /root/output/php.js && \
Expand Down
37 changes: 37 additions & 0 deletions packages/php-wasm/compile/build-assets/esm-prefix.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,8 @@ if (PHPLoader.debug && typeof Asyncify !== "undefined") {
return originalHandleSleep(startAsync);
}
}

return PHPLoader;

// Close the opening bracket from esm-prefix.js:
}
49 changes: 45 additions & 4 deletions packages/php-wasm/node/public/php_5_6.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
export const dependenciesTotalSize = 10176855;
const dependencyFilename = __dirname + '/php_5_6.wasm';
export { dependencyFilename }; export function init(RuntimeName, PHPLoader) {
export { dependencyFilename };
export const dependenciesTotalSize = 10176855;
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
var Module = typeof PHPLoader != "undefined" ? PHPLoader : {};

var moduleOverrides = Object.assign({}, Module);
Expand Down Expand Up @@ -7041,7 +7078,7 @@ DNS.address_map.addrs.localhost = '127.0.0.1';
* so that it can be inspected later.
*/
PHPLoader.debug = 'debug' in PHPLoader ? PHPLoader.debug : true;
if (PHPLoader.debug) {
if (PHPLoader.debug && typeof Asyncify !== "undefined") {
const originalHandleSleep = Asyncify.handleSleep;
Asyncify.handleSleep = function (startAsync) {
if (!ABORT) {
Expand All @@ -7050,4 +7087,8 @@ if (PHPLoader.debug) {
return originalHandleSleep(startAsync);
}
}
return PHPLoader; }

return PHPLoader;

// Close the opening bracket from esm-prefix.js:
}
Binary file modified packages/php-wasm/node/public/php_5_6.wasm
Binary file not shown.
49 changes: 45 additions & 4 deletions packages/php-wasm/node/public/php_7_0.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
export const dependenciesTotalSize = 10390258;
const dependencyFilename = __dirname + '/php_7_0.wasm';
export { dependencyFilename }; export function init(RuntimeName, PHPLoader) {
export { dependencyFilename };
export const dependenciesTotalSize = 10390259;
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
var Module = typeof PHPLoader != "undefined" ? PHPLoader : {};

var moduleOverrides = Object.assign({}, Module);
Expand Down Expand Up @@ -7022,7 +7059,7 @@ DNS.address_map.addrs.localhost = '127.0.0.1';
* so that it can be inspected later.
*/
PHPLoader.debug = 'debug' in PHPLoader ? PHPLoader.debug : true;
if (PHPLoader.debug) {
if (PHPLoader.debug && typeof Asyncify !== "undefined") {
const originalHandleSleep = Asyncify.handleSleep;
Asyncify.handleSleep = function (startAsync) {
if (!ABORT) {
Expand All @@ -7031,4 +7068,8 @@ if (PHPLoader.debug) {
return originalHandleSleep(startAsync);
}
}
return PHPLoader; }

return PHPLoader;

// Close the opening bracket from esm-prefix.js:
}
Binary file modified packages/php-wasm/node/public/php_7_0.wasm
Binary file not shown.
49 changes: 45 additions & 4 deletions packages/php-wasm/node/public/php_7_1.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
export const dependenciesTotalSize = 10583885;
const dependencyFilename = __dirname + '/php_7_1.wasm';
export { dependencyFilename }; export function init(RuntimeName, PHPLoader) {
export { dependencyFilename };
export const dependenciesTotalSize = 10583885;
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
var Module = typeof PHPLoader != "undefined" ? PHPLoader : {};

var moduleOverrides = Object.assign({}, Module);
Expand Down Expand Up @@ -7007,7 +7044,7 @@ DNS.address_map.addrs.localhost = '127.0.0.1';
* so that it can be inspected later.
*/
PHPLoader.debug = 'debug' in PHPLoader ? PHPLoader.debug : true;
if (PHPLoader.debug) {
if (PHPLoader.debug && typeof Asyncify !== "undefined") {
const originalHandleSleep = Asyncify.handleSleep;
Asyncify.handleSleep = function (startAsync) {
if (!ABORT) {
Expand All @@ -7016,4 +7053,8 @@ if (PHPLoader.debug) {
return originalHandleSleep(startAsync);
}
}
return PHPLoader; }

return PHPLoader;

// Close the opening bracket from esm-prefix.js:
}
Binary file modified packages/php-wasm/node/public/php_7_1.wasm
Binary file not shown.
49 changes: 45 additions & 4 deletions packages/php-wasm/node/public/php_7_2.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,43 @@
export const dependenciesTotalSize = 10993354;
const dependencyFilename = __dirname + '/php_7_2.wasm';
export { dependencyFilename }; export function init(RuntimeName, PHPLoader) {
export { dependencyFilename };
export const dependenciesTotalSize = 10993354;
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
var Module = typeof PHPLoader != "undefined" ? PHPLoader : {};

var moduleOverrides = Object.assign({}, Module);
Expand Down Expand Up @@ -7023,7 +7060,7 @@ DNS.address_map.addrs.localhost = '127.0.0.1';
* so that it can be inspected later.
*/
PHPLoader.debug = 'debug' in PHPLoader ? PHPLoader.debug : true;
if (PHPLoader.debug) {
if (PHPLoader.debug && typeof Asyncify !== "undefined") {
const originalHandleSleep = Asyncify.handleSleep;
Asyncify.handleSleep = function (startAsync) {
if (!ABORT) {
Expand All @@ -7032,4 +7069,8 @@ if (PHPLoader.debug) {
return originalHandleSleep(startAsync);
}
}
return PHPLoader; }

return PHPLoader;

// Close the opening bracket from esm-prefix.js:
}
Binary file modified packages/php-wasm/node/public/php_7_2.wasm
Binary file not shown.
Loading

0 comments on commit 01357b9

Please sign in to comment.