Remove old monkey patch

This commit is contained in:
Samuel Elliott 2018-03-31 00:47:25 +01:00
parent 68af931128
commit 74d78f4bca
No known key found for this signature in database
GPG Key ID: 8420C7CDE43DC4D6
3 changed files with 3 additions and 258 deletions

View File

@ -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)
};
}

View File

@ -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);
}
}

View File

@ -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