syscall: on wasm, do not panic if "process" global is not defined

When running wasm in the browser, the "process" global is not defined.
This causes functions like os.Getpid() to panic, which is unusual.
For example on Windows os.Getpid() returns -1 and does not panic.

This change adds a dummy polyfill for "process" which returns -1 or an
error. It also extends the polyfill for "fs".

Fixes #34627
Replaces CL 199357

Change-Id: Ifeb12fe7e152c517848933a9ab5f6f749896dcef
Reviewed-on: https://go-review.googlesource.com/c/go/+/199698
Run-TryBot: Richard Musiol <neelance@gmail.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/misc/wasm/wasm_exec.js b/misc/wasm/wasm_exec.js
index 9ffa920..3c2c186 100644
--- a/misc/wasm/wasm_exec.js
+++ b/misc/wasm/wasm_exec.js
@@ -30,6 +30,12 @@
 		global.fs = require("fs");
 	}
 
+	const enosys = () => {
+		const err = new Error("not implemented");
+		err.code = "ENOSYS";
+		return err;
+	};
+
 	if (!global.fs) {
 		let outputBuf = "";
 		global.fs = {
@@ -45,27 +51,53 @@
 			},
 			write(fd, buf, offset, length, position, callback) {
 				if (offset !== 0 || length !== buf.length || position !== null) {
-					throw new Error("not implemented");
+					callback(enosys());
+					return;
 				}
 				const n = this.writeSync(fd, buf);
 				callback(null, n);
 			},
-			open(path, flags, mode, callback) {
-				const err = new Error("not implemented");
-				err.code = "ENOSYS";
-				callback(err);
-			},
-			read(fd, buffer, offset, length, position, callback) {
-				const err = new Error("not implemented");
-				err.code = "ENOSYS";
-				callback(err);
-			},
-			fsync(fd, callback) {
-				callback(null);
-			},
+			chmod(path, mode, callback) { callback(enosys()); },
+			chown(path, uid, gid, callback) { callback(enosys()); },
+			close(fd, callback) { callback(enosys()); },
+			fchmod(fd, mode, callback) { callback(enosys()); },
+			fchown(fd, uid, gid, callback) { callback(enosys()); },
+			fstat(fd, callback) { callback(enosys()); },
+			fsync(fd, callback) { callback(null); },
+			ftruncate(fd, length, callback) { callback(enosys()); },
+			lchown(path, uid, gid, callback) { callback(enosys()); },
+			link(path, link, callback) { callback(enosys()); },
+			lstat(path, callback) { callback(enosys()); },
+			mkdir(path, perm, callback) { callback(enosys()); },
+			open(path, flags, mode, callback) { callback(enosys()); },
+			read(fd, buffer, offset, length, position, callback) { callback(enosys()); },
+			readdir(path, callback) { callback(enosys()); },
+			readlink(path, callback) { callback(enosys()); },
+			rename(from, to, callback) { callback(enosys()); },
+			rmdir(path, callback) { callback(enosys()); },
+			stat(path, callback) { callback(enosys()); },
+			symlink(path, link, callback) { callback(enosys()); },
+			truncate(path, length, callback) { callback(enosys()); },
+			unlink(path, callback) { callback(enosys()); },
+			utimes(path, atime, mtime, callback) { callback(enosys()); },
 		};
 	}
 
+	if (!global.process) {
+		global.process = {
+			getuid() { return -1; },
+			getgid() { return -1; },
+			geteuid() { return -1; },
+			getegid() { return -1; },
+			getgroups() { throw enosys(); },
+			pid: -1,
+			ppid: -1,
+			umask() { throw enosys(); },
+			cwd() { throw enosys(); },
+			chdir() { throw enosys(); },
+		}
+	}
+
 	if (!global.crypto) {
 		const nodeCrypto = require("crypto");
 		global.crypto = {
diff --git a/src/syscall/syscall_js.go b/src/syscall/syscall_js.go
index 987dd4a..dfb4a27 100644
--- a/src/syscall/syscall_js.go
+++ b/src/syscall/syscall_js.go
@@ -303,9 +303,10 @@
 	return jsProcess.Call("getegid").Int()
 }
 
-func Getgroups() ([]int, error) {
+func Getgroups() (groups []int, err error) {
+	defer recoverErr(&err)
 	array := jsProcess.Call("getgroups")
-	groups := make([]int, array.Length())
+	groups = make([]int, array.Length())
 	for i := range groups {
 		groups[i] = array.Index(i).Int()
 	}