/******************************************************************************* uBlock Origin - a comprehensive, efficient content blocker Copyright (C) 2019-present Raymond Hill This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see {http://www.gnu.org/licenses/}. Home: https://github.com/gorhill/uBlock */ import { registerScriptlet } from './base.js'; /******************************************************************************/ export function proxyApplyFn( target = '', handler = '' ) { let context = globalThis; let prop = target; for (;;) { const pos = prop.indexOf('.'); if ( pos === -1 ) { break; } context = context[prop.slice(0, pos)]; if ( context instanceof Object === false ) { return; } prop = prop.slice(pos+1); } const fn = context[prop]; if ( typeof fn !== 'function' ) { return; } if ( proxyApplyFn.CtorContext === undefined ) { proxyApplyFn.ctorContexts = []; proxyApplyFn.CtorContext = class { constructor(...args) { this.init(...args); } init(callFn, callArgs) { this.callFn = callFn; this.callArgs = callArgs; return this; } reflect() { const r = Reflect.construct(this.callFn, this.callArgs); this.callFn = this.callArgs = this.private = undefined; proxyApplyFn.ctorContexts.push(this); return r; } static factory(...args) { return proxyApplyFn.ctorContexts.length !== 0 ? proxyApplyFn.ctorContexts.pop().init(...args) : new proxyApplyFn.CtorContext(...args); } }; proxyApplyFn.applyContexts = []; proxyApplyFn.ApplyContext = class { constructor(...args) { this.init(...args); } init(callFn, thisArg, callArgs) { this.callFn = callFn; this.thisArg = thisArg; this.callArgs = callArgs; return this; } reflect() { const r = Reflect.apply(this.callFn, this.thisArg, this.callArgs); this.callFn = this.thisArg = this.callArgs = this.private = undefined; proxyApplyFn.applyContexts.push(this); return r; } static factory(...args) { return proxyApplyFn.applyContexts.length !== 0 ? proxyApplyFn.applyContexts.pop().init(...args) : new proxyApplyFn.ApplyContext(...args); } }; } const fnStr = fn.toString(); const toString = (function toString() { return fnStr; }).bind(null); const proxyDetails = { apply(target, thisArg, args) { return handler(proxyApplyFn.ApplyContext.factory(target, thisArg, args)); }, get(target, prop) { if ( prop === 'toString' ) { return toString; } return Reflect.get(target, prop); }, }; if ( fn.prototype?.constructor === fn ) { proxyDetails.construct = function(target, args) { return handler(proxyApplyFn.CtorContext.factory(target, args)); }; } context[prop] = new Proxy(fn, proxyDetails); } registerScriptlet(proxyApplyFn, { name: 'proxy-apply.fn', });