Skip to content

Commit 2037020

Browse files
committed
Run the entire Blueprint!
1 parent a611281 commit 2037020

File tree

6 files changed

+302
-215
lines changed

6 files changed

+302
-215
lines changed

packages/php-wasm/compile/php/phpwasm-emscripten-library.js

Lines changed: 121 additions & 103 deletions
Original file line numberDiff line numberDiff line change
@@ -19,43 +19,43 @@ const LibraryExample = {
1919
PHPWASM.EventEmitter = ENVIRONMENT_IS_NODE
2020
? require('events').EventEmitter
2121
: class EventEmitter {
22-
constructor() {
23-
this.listeners = {};
24-
}
25-
emit(eventName, data) {
26-
if (this.listeners[eventName]) {
27-
this.listeners[eventName].forEach(
28-
(callback) => {
29-
callback(data);
30-
}
31-
);
32-
}
22+
constructor() {
23+
this.listeners = {};
24+
}
25+
emit(eventName, data) {
26+
if (this.listeners[eventName]) {
27+
this.listeners[eventName].forEach(
28+
(callback) => {
29+
callback(data);
30+
}
31+
);
3332
}
34-
once(eventName, callback) {
35-
const self = this;
36-
function removedCallback() {
37-
callback(...arguments);
38-
self.removeListener(eventName, removedCallback);
39-
}
40-
this.on(eventName, removedCallback);
33+
}
34+
once(eventName, callback) {
35+
const self = this;
36+
function removedCallback() {
37+
callback(...arguments);
38+
self.removeListener(eventName, removedCallback);
4139
}
42-
removeAllListeners(eventName) {
43-
if (eventName) {
44-
delete this.listeners[eventName];
45-
} else {
46-
this.listeners = {};
47-
}
40+
this.on(eventName, removedCallback);
41+
}
42+
removeAllListeners(eventName) {
43+
if (eventName) {
44+
delete this.listeners[eventName];
45+
} else {
46+
this.listeners = {};
4847
}
49-
removeListener(eventName, callback) {
50-
if (this.listeners[eventName]) {
51-
const idx =
52-
this.listeners[eventName].indexOf(callback);
53-
if (idx !== -1) {
54-
this.listeners[eventName].splice(idx, 1);
55-
}
48+
}
49+
removeListener(eventName, callback) {
50+
if (this.listeners[eventName]) {
51+
const idx =
52+
this.listeners[eventName].indexOf(callback);
53+
if (idx !== -1) {
54+
this.listeners[eventName].splice(idx, 1);
5655
}
5756
}
58-
};
57+
}
58+
};
5959
PHPWASM.child_proc_by_fd = {};
6060
PHPWASM.child_proc_by_pid = {};
6161
PHPWASM.input_devices = {};
@@ -180,7 +180,7 @@ const LibraryExample = {
180180
};
181181
return [promise, cancel];
182182
},
183-
noop: function () {},
183+
noop: function () { },
184184

185185
spawnProcess: function (command, args, options) {
186186
if (Module['spawnProcess']) {
@@ -205,8 +205,8 @@ const LibraryExample = {
205205
}
206206
const e = new Error(
207207
'popen(), proc_open() etc. are unsupported in the browser. Call php.setSpawnHandler() ' +
208-
'and provide a callback to handle spawning processes, or disable a popen(), proc_open() ' +
209-
'and similar functions via php.ini.'
208+
'and provide a callback to handle spawning processes, or disable a popen(), proc_open() ' +
209+
'and similar functions via php.ini.'
210210
);
211211
e.code = 'SPAWN_UNSUPPORTED';
212212
throw e;
@@ -256,7 +256,7 @@ const LibraryExample = {
256256
const device = FS.createDevice(
257257
'/dev',
258258
filename,
259-
function () {},
259+
function () { },
260260
function (byte) {
261261
try {
262262
dataBuffer.push(byte);
@@ -323,7 +323,7 @@ const LibraryExample = {
323323
argsArray.push(UTF8ToString(HEAPU32[charPointer >> 2]));
324324
}
325325
}
326-
326+
327327
const cwdstr = cwdPtr ? UTF8ToString(cwdPtr) : null;
328328
let envObject = null;
329329

@@ -449,7 +449,7 @@ const LibraryExample = {
449449
stderrAt += data.length;
450450
});
451451
cp.stderr.on('end', function (data) {
452-
FS.close(stderrStream);
452+
FS.close(stdoutStream);
453453
});
454454
}
455455

@@ -534,13 +534,14 @@ const LibraryExample = {
534534
return -1;
535535
}
536536
if (PHPWASM.child_proc_by_pid[pid].exited) {
537-
HEAPU32[exitCodePtr >> 2] =
538-
PHPWASM.child_proc_by_pid[pid].exitCode;
537+
HEAPU32[exitCodePtr >> 2] =
538+
PHPWASM.child_proc_by_pid[pid].exitCode;
539539
return 1;
540540
}
541541
return 0;
542542
},
543543

544+
544545
js_waitpid: function (pid, exitCodePtr) {
545546
if (!PHPWASM.child_proc_by_pid[pid]) {
546547
return -1;
@@ -549,7 +550,7 @@ const LibraryExample = {
549550
const poll = function () {
550551
if (PHPWASM.child_proc_by_pid[pid]?.exited) {
551552
HEAPU32[exitCodePtr >> 2] =
552-
PHPWASM.child_proc_by_pid[pid].exitCode;
553+
PHPWASM.child_proc_by_pid[pid].exitCode;
553554
wakeUp(pid);
554555
} else {
555556
setTimeout(poll, 50);
@@ -760,74 +761,91 @@ const LibraryExample = {
760761
* @see https://github.com/emscripten-core/emscripten/issues/13214
761762
*/
762763
js_fd_read: function (fd, iov, iovcnt, pnum) {
763-
var returnCode;
764-
var stream;
765-
try {
766-
stream = SYSCALLS.getStreamFromFD(fd);
767-
var num = doReadv(stream, iov, iovcnt);
768-
HEAPU32[pnum >> 2] = num;
769-
returnCode = 0;
770-
} catch (e) {
771-
if (typeof FS == 'undefined' || !(e.name === 'ErrnoError')) throw e;
772-
returnCode = e.errno;
764+
// Only run the read operation on a regular call,
765+
// never when rewinding the stack.
766+
if (Asyncify.state === Asyncify.State.Normal) {
767+
var returnCode;
768+
var stream;
769+
let num = 0;
770+
try {
771+
stream = SYSCALLS.getStreamFromFD(fd);
772+
const num = doReadv(stream, iov, iovcnt);
773+
HEAPU32[pnum >> 2] = num;
774+
return 0;
775+
} catch (e) {
776+
// Rethrow any unexpected non-filesystem errors.
777+
if (typeof FS == "undefined" || !(e.name === "ErrnoError")) {
778+
throw e;
779+
}
780+
// Only return synchronously if this isn't an asynchronous pipe.
781+
// Error code 6 indicates EWOULDBLOCK – this is our signal to wait.
782+
// We also need to distinguish between a process pipe and a file pipe, otherwise
783+
// reading from an empty file would block until the timeout.
784+
if (e.errno !== 6 || !(stream?.fd in PHPWASM.child_proc_by_fd)) {
785+
// On failure, yield 0 bytes read to indicate EOF.
786+
HEAPU32[pnum >> 2] = 0;
787+
return returnCode
788+
}
789+
}
773790
}
774791

775-
// If it's a blocking process pipe, wait for it to become readable.
776-
// We need to distinguish between a process pipe and a file pipe, otherwise
777-
// reading from an empty file would block until the timeout.
778-
if (
779-
returnCode === 6 /*EWOULDBLOCK*/ &&
780-
stream?.fd in PHPWASM.child_proc_by_fd
781-
) {
782-
// You might wonder why we duplicate the code here instead of always using
783-
// Asyncify.handleSleep(). The reason is performance. Most of the time,
784-
// the read operation will work synchronously and won't require yielding
785-
// back to JS. In these cases we don't want to pay the Asyncify overhead,
786-
// save the stack, yield back to JS, restore the stack etc.
787-
return Asyncify.handleSleep(function (wakeUp) {
788-
var retries = 0;
789-
var interval = 50;
790-
var timeout = 5000;
791-
// We poll for data and give up after a timeout.
792-
// We can't simply rely on PHP timeout here because we don't want
793-
// to, say, block the entire PHPUnit test suite without any visible
794-
// feedback.
795-
var maxRetries = timeout / interval;
796-
function poll() {
797-
var returnCode;
798-
var stream;
799-
try {
800-
stream = SYSCALLS.getStreamFromFD(fd);
801-
var num = doReadv(stream, iov, iovcnt);
802-
HEAPU32[pnum >> 2] = num;
803-
returnCode = 0;
804-
} catch (e) {
805-
if (
806-
typeof FS == 'undefined' ||
807-
!(e.name === 'ErrnoError')
808-
) {
809-
console.error(e);
810-
throw e;
811-
}
812-
returnCode = e.errno;
813-
}
814-
792+
// At this point we know we have to poll.
793+
// You might wonder why we duplicate the code here instead of always using
794+
// Asyncify.handleSleep(). The reason is performance. Most of the time,
795+
// the read operation will work synchronously and won't require yielding
796+
// back to JS. In these cases we don't want to pay the Asyncify overhead,
797+
// save the stack, yield back to JS, restore the stack etc.
798+
return Asyncify.handleSleep(function (wakeUp) {
799+
var retries = 0;
800+
var interval = 50;
801+
var timeout = 5000;
802+
// We poll for data and give up after a timeout.
803+
// We can't simply rely on PHP timeout here because we don't want
804+
// to, say, block the entire PHPUnit test suite without any visible
805+
// feedback.
806+
var maxRetries = timeout / interval;
807+
function poll() {
808+
var returnCode;
809+
var stream;
810+
let num;
811+
try {
812+
stream = SYSCALLS.getStreamFromFD(fd);
813+
num = doReadv(stream, iov, iovcnt);
814+
returnCode = 0;
815+
} catch (e) {
815816
if (
816-
returnCode !== 6 ||
817-
++retries > maxRetries ||
818-
!(fd in PHPWASM.child_proc_by_fd) ||
819-
PHPWASM.child_proc_by_fd[fd]?.exited ||
820-
FS.isClosed(stream)
817+
typeof FS == 'undefined' ||
818+
!(e.name === 'ErrnoError')
821819
) {
822-
wakeUp(returnCode);
823-
} else {
824-
setTimeout(poll, interval);
820+
console.error(e);
821+
throw e;
825822
}
823+
returnCode = e.errno;
826824
}
827-
poll();
828-
});
829-
}
830-
return returnCode;
825+
826+
const success = returnCode === 0;
827+
const failure = (
828+
++retries > maxRetries ||
829+
!(fd in PHPWASM.child_proc_by_fd) ||
830+
PHPWASM.child_proc_by_fd[fd]?.exited ||
831+
FS.isClosed(stream)
832+
);
833+
834+
if (success) {
835+
HEAPU32[pnum >> 2] = num;
836+
wakeUp(0);
837+
} else if (failure) {
838+
// On failure, yield 0 bytes read to indicate EOF.
839+
HEAPU32[pnum >> 2] = 0;
840+
// If the failure is due to a timeout, return 0 to indicate that we
841+
// reached EOF. Otherwise, propagate the error code.
842+
wakeUp(returnCode === 6 ? 0 : returnCode);
843+
} else {
844+
setTimeout(poll, interval);
845+
}
846+
}
847+
poll();
848+
});
831849
},
832850

833851
/**

packages/php-wasm/universal/src/lib/base-php.ts

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -855,11 +855,15 @@ export function normalizeHeaders(
855855

856856
type EmscriptenFS = any;
857857

858-
export function syncFSTo(source: BasePHP, target: BasePHP) {
858+
export function syncFSTo(
859+
source: BasePHP,
860+
target: BasePHP,
861+
path: string | null = null
862+
) {
859863
copyFS(
860864
source[__private__dont__use].FS,
861865
target[__private__dont__use].FS,
862-
source.documentRoot
866+
path ?? source.documentRoot
863867
);
864868
}
865869

@@ -887,8 +891,8 @@ export function copyFS(
887891
// Let's be extra careful and only proceed if newFs doesn't
888892
// already have a node at the given path.
889893
try {
890-
target = target.lookupPath(path);
891-
return;
894+
// target = target.lookupPath(path);
895+
// return;
892896
} catch (e) {
893897
// There's no such node in the new FS. Good,
894898
// we may proceed.

0 commit comments

Comments
 (0)