diff --git a/client/src/modules/pluginapi.js b/client/src/modules/pluginapi.js index eb0499e8..f8745b96 100644 --- a/client/src/modules/pluginapi.js +++ b/client/src/modules/pluginapi.js @@ -83,14 +83,13 @@ export default class PluginApi { get Utils() { return { overload: () => Utils.overload.apply(Utils, arguments), - monkeyPatch: () => Utils.monkeyPatch.apply(Utils, arguments), - monkeyPatchOnce: () => Utils.monkeyPatchOnce.apply(Utils, arguments), - compatibleMonkeyPatch: () => Utils.monkeyPatchOnce.apply(Utils, arguments), tryParseJson: () => Utils.tryParseJson.apply(Utils, arguments), toCamelCase: () => Utils.toCamelCase.apply(Utils, arguments), compare: () => Utils.compare.apply(Utils, arguments), deepclone: () => Utils.deepclone.apply(Utils, arguments), - deepfreeze: () => Utils.deepfreeze.apply(Utils, arguments) + deepfreeze: () => Utils.deepfreeze.apply(Utils, arguments), + removeFromArray: () => Utils.removeFromArray.apply(Utils, arguments), + defineSoftGetter: () => Utils.defineSoftGetter.apply(Utils, arguments) }; } diff --git a/common/modules/monkeypatch.js b/common/modules/monkeypatch.js deleted file mode 100644 index a7fe0da0..00000000 --- a/common/modules/monkeypatch.js +++ /dev/null @@ -1,169 +0,0 @@ -/** - * BetterDiscord Monkeypatch - * Copyright (c) 2015-present Jiiks/JsSucks - https://github.com/Jiiks / https://github.com/JsSucks - * All rights reserved. - * https://betterdiscord.net - * - * This source code is licensed under the MIT license found in the - * LICENSE file in the root directory of this source tree. -*/ - -import { ClientLogger as Logger } from './logger'; - -const patchedFunctions = new WeakMap(); - -export class PatchedFunction { - constructor(object, methodName, replaceOriginal = true) { - if (patchedFunctions.has(object[methodName])) { - const patchedFunction = patchedFunctions.get(object[methodName]); - if (replaceOriginal) patchedFunction.replaceOriginal(); - return patchedFunction; - } - - this.object = object; - this.methodName = methodName; - this.patches = []; - this.originalMethod = object[methodName]; - this.replaced = false; - - const patchedFunction = this; - this.replace = function(...args) { - patchedFunction.call(this, arguments); - }; - - patchedFunctions.set(object[methodName], this); - patchedFunctions.set(this.replace, this); - - if (replaceOriginal) - this.replaceOriginal(); - } - - addPatch(patch) { - if (!this.patches.includes(patch)) - this.patches.push(patch); - } - - removePatch(patch, restoreOriginal = true) { - let i = 0; - while (this.patches[i]) { - if (this.patches[i] !== patch) i++; - else this.patches.splice(i, 1); - } - - if (!this.patches.length && restoreOriginal) - this.restoreOriginal(); - } - - replaceOriginal() { - if (this.replaced) return; - this.object[this.methodName] = Object.assign(this.replace, this.object[this.methodName]); - this.replaced = true; - } - - restoreOriginal() { - if (!this.replaced) return; - this.object[this.methodName] = Object.assign(this.object[this.methodName], this.replace); - this.replaced = false; - } - - call(_this, args) { - const data = { - this: _this, - arguments: args, - return: undefined, - originalMethod: this.originalMethod, - callOriginalMethod: () => { - Logger.log('MonkeyPatch', [`Calling original method`, this, data]); - data.return = this.originalMethod.apply(data.this, data.arguments); - } - }; - - // Work through the patches calling each patch's hooks as if each patch had overridden the previous patch - for (let patch of this.patches) { - let callOriginalMethod = data.callOriginalMethod; - data.callOriginalMethod = () => { - const patch_data = Object.assign({}, data, { - callOriginalMethod, patch - }); - patch.call(patch_data); - data.arguments = patch_data.arguments; - data.return = patch_data.return; - }; - } - - data.callOriginalMethod(); - return data.return; - } -} - -export class Patch { - constructor(patchedFunction, options, f) { - this.patchedFunction = patchedFunction; - - if (options instanceof Function) { - f = options; - options = { - instead: data => { - f.call(this, data, ...data.arguments); - } - }; - } else if (options === 'before') { - options = { - before: data => { - f.call(this, data, ...data.arguments); - } - }; - } else if (options === 'after') { - options = { - after: data => { - f.call(this, data, ...data.arguments); - } - }; - } - - this.before = options.before || undefined; - this.instead = options.instead || undefined; - this.after = options.after || undefined; - this.once = options.once || false; - this.silent = options.silent || false; - this.suppressErrors = typeof options.suppressErrors === 'boolean' ? options.suppressErrors : true; - } - - call(data) { - if (this.once) - this.cancel(); - - this.callBefore(data); - this.callInstead(data); - this.callAfter(data); - } - - callBefore(data) { - if (this.before) - this.callHook('before', this.before, data); - } - - callInstead(data) { - if (this.instead) - this.callHook('instead', this.instead, data); - else data.callOriginalMethod(); - } - - callAfter(data) { - if (this.after) - this.callHook('after', this.after, data); - } - - callHook(hook, f, data) { - try { - f.call(this, data, ...data.arguments); - } catch (err) { - Logger.log('MonkeyPatch', [`Error thrown in ${hook} hook of`, this, '- :', err]); - if (!this.suppressErrors) throw err; - } - } - - cancel() { - this.patchedFunction.removePatch(this); - } -} diff --git a/common/modules/utils.js b/common/modules/utils.js index 07b46bbf..9e639b9f 100644 --- a/common/modules/utils.js +++ b/common/modules/utils.js @@ -8,7 +8,6 @@ * LICENSE file in the root directory of this source tree. */ -import { PatchedFunction, Patch } from './monkeypatch'; import path from 'path'; import fs from 'fs'; import _ from 'lodash'; @@ -23,90 +22,6 @@ export class Utils { } } - /** - * Monkey-patches an object's method. - * @param {Object} object The object containing the function to monkey patch - * @param {String} methodName The name of the method to monkey patch - * @param {Object|String|Function} options Options to pass to the Patch constructor - * @param {Function} function If {options} is either "before" or "after", this function will be used as that hook - */ - static monkeyPatch(object, methodName, options, f) { - const patchedFunction = new PatchedFunction(object, methodName); - const patch = new Patch(patchedFunction, options, f); - patchedFunction.addPatch(patch); - return patch; - } - - /** - * Monkey-patches an object's method and returns a promise that will be resolved with the data object when the method is called. - * This can only be used to get the arguments and return data. If you want to change anything, call Utils.monkeyPatch with the once option set to true. - */ - static monkeyPatchOnce(object, methodName) { - return new Promise((resolve, reject) => { - this.monkeyPatch(object, methodName, 'after', data => { - data.patch.cancel(); - resolve(data); - }); - }); - } - - /** - * Monkey-patches an object's method and returns a promise that will be resolved with the data object when the method is called. - * You will have to call data.callOriginalMethod() if you wants the original method to be called. - */ - static monkeyPatchAsync(object, methodName, callback) { - return new Promise((resolve, reject) => { - this.monkeyPatch(object, methodName, data => { - data.patch.cancel(); - - data.promise = data.return = callback ? Promise.all(callback.call(global, data, ...data.arguments)) : new Promise((resolve, reject) => { - data.resolve = resolve; - data.reject = reject; - }); - - resolve(data); - }); - }); - } - - /** - * Monkeypatch function that is compatible with samogot's Lib Discord Internals. - * Don't use this for writing new plugins as it will eventually be removed! - */ - static compatibleMonkeyPatch(what, methodName, options) { - const { before, instead, after, once = false, silent = false } = options; - const cancelPatch = () => patch.cancel(); - - const compatible_function = _function => data => { - const compatible_data = { - thisObject: data.this, - methodArguments: data.arguments, - returnValue: data.return, - cancelPatch, - originalMethod: data.originalMethod, - callOriginalMethod: () => data.callOriginalMethod() - }; - try { - _function(compatible_data); - data.arguments = compatible_data.methodArguments; - data.return = compatible_data.returnValue; - } catch (err) { - data.arguments = compatible_data.methodArguments; - data.return = compatible_data.returnValue; - throw err; - } - }; - - const patch = this.monkeyPatch(what, methodName, { - before: !instead && before ? compatible_function(before) : undefined, - instead: instead ? compatible_function(instead) : undefined, - after: !instead && after ? compatible_function(after) : undefined, - once - }); - - return cancelPatch; - } - /** * Attempts to parse a string as JSON. * @param {String} json The string to parse