From a6b7474a6bd23bbceebbd4a0e1d941768572a1c0 Mon Sep 17 00:00:00 2001 From: Toad06 Date: Sun, 20 Dec 2020 16:06:50 +0100 Subject: [PATCH] web: Prevent crashes with external libraries --- web/packages/core/src/js-polyfills.ts | 53 +++++++++++++++++++++++++++ web/packages/core/src/load-ruffle.ts | 13 ++++++- 2 files changed, 65 insertions(+), 1 deletion(-) create mode 100644 web/packages/core/src/js-polyfills.ts diff --git a/web/packages/core/src/js-polyfills.ts b/web/packages/core/src/js-polyfills.ts new file mode 100644 index 000000000..93d0f218e --- /dev/null +++ b/web/packages/core/src/js-polyfills.ts @@ -0,0 +1,53 @@ +/* eslint @typescript-eslint/no-explicit-any: "off" */ + +/** + * Polyfills the `Array.prototype.reduce` method. + * + * Production steps of ECMA-262, Edition 5, 15.4.4.21 + * Reference: https://es5.github.io/#x15.4.4.21 + * https://tc39.github.io/ecma262/#sec-array.prototype.reduce + * + */ +export function setArrayPrototypeReduce(): void { + Object.defineProperty(Array.prototype, "reduce", { + value: function (...args: any) { + const callback = args[0]; + if (this === null) { + throw new TypeError( + "Array.prototype.reduce called on null or undefined" + ); + } + if (typeof callback !== "function") { + throw new TypeError(`${callback} is not a function`); + } + + const o = Object(this); + const len = o.length >>> 0; + let k = 0; + let value; + + if (args.length >= 2) { + value = args[1]; + } else { + while (k < len && !(k in o)) { + k++; + } + if (k >= len) { + throw new TypeError( + "Reduce of empty array with no initial value" + ); + } + value = o[k++]; + } + + while (k < len) { + if (k in o) { + value = callback(value, o[k], k, o); + } + k++; + } + + return value; + }, + }); +} diff --git a/web/packages/core/src/load-ruffle.ts b/web/packages/core/src/load-ruffle.ts index 91bc5a36d..c37461a3d 100644 --- a/web/packages/core/src/load-ruffle.ts +++ b/web/packages/core/src/load-ruffle.ts @@ -6,6 +6,8 @@ import { Ruffle } from "../pkg/ruffle_web"; +import { setArrayPrototypeReduce } from "./js-polyfills"; + /** * Load ruffle from an automatically-detected location. * @@ -17,12 +19,21 @@ import { Ruffle } from "../pkg/ruffle_web"; * instances. */ async function fetchRuffle(): Promise<{ new (...args: any[]): Ruffle }> { + if ( + typeof Array.prototype.reduce !== "function" || + Array.prototype.reduce.toString().indexOf("[native code]") === -1 + ) { + // Some external libraries override the `Array.prototype.reduce` method in a way + // that causes Webpack to crash (#1507, #1865), so we need to override it again. + setArrayPrototypeReduce(); + } + try { // If ruffleRuntimePath is defined then we are executing inside the extension // closure. In that case, we configure our local Webpack instance. __webpack_public_path__ = ruffleRuntimePath + "dist/"; } catch (e) { - // Checking an undefined closure variable usually throws ReferencError, + // Checking an undefined closure variable usually throws ReferenceError, // so we need to catch it here and continue onward. if (!(e instanceof ReferenceError)) { throw e;