From 111c29d15007de569c09cb826dffda1cae410a35 Mon Sep 17 00:00:00 2001 From: ezsh <ezsh.junk@gmail.com> Date: Mon, 8 Nov 2021 17:30:25 +0100 Subject: [PATCH] Extend readonly proxy support to cover Set and Map When an RA rule wants to access game state, it is accessed via a readon;y proxy. The proxy handles methods for arrays only and fails when the rule tries to access methods of other classes (e.g. JobIDMap contains sets). SugarCube supports arrays, sets, maps, the date class and generic objects in the game state. Therefore the proxy has to be extended to support Set and Map classes too. --- src/004-base/000-proxies.js | 65 ++++++++++++++++++++----------------- 1 file changed, 36 insertions(+), 29 deletions(-) diff --git a/src/004-base/000-proxies.js b/src/004-base/000-proxies.js index 69b72acd7d6..9738e68bf06 100644 --- a/src/004-base/000-proxies.js +++ b/src/004-base/000-proxies.js @@ -1,43 +1,50 @@ (function() { const readOnlySymbol = Symbol("readonly proxy"); + const dummy = () => true; + + /** + * Creates a readonly proxy for all the target properties except banned methods + * @param {any} target + * @param {string[]} bannedMethodNames The list of method names that are banned and can't be accesses via the proxy + * @param {string} errorMessage message for throwing exceptions + */ + const createProxyWithBannedMethods = (target, bannedMethodNames, errorMessage) => { + return new Proxy(target, { + get: function(o, prop) { + if (prop === readOnlySymbol) { return true; } + const val = o[prop]; + if (typeof val === 'function') { + if (bannedMethodNames.includes(prop)) { + return function() { + throw Error(errorMessage); + }; + } + return val.bind(target); + } + return createReadonlyProxy(val); + }, + set: dummy, + deleteProperty: dummy + }); + }; + globalThis.createReadonlyProxy = function(target) { if (target == null) { return target; } // intentionally == if (target[readOnlySymbol]) { return target; } if (_.isArray(target)) { - return new Proxy(target, { - get: function(o, prop) { - if (prop === readOnlySymbol) { return true; } - const val = o[prop]; - if (typeof val === 'function') { - if (['push', 'unshift', 'pop'].includes(prop)) { - return function() { - throw Error("Cannot modify a readonly array"); - }; - } - return val.bind(target); - } - return createReadonlyProxy(val); - }, - set: function(o, prop, value) { - return true; - }, - deleteProperty: function(o, prop) { - return true; - } - }); - } - if (_.isObject(target)) { + return createProxyWithBannedMethods(target, ['push', 'unshift', 'pop'], "Cannot modify a readonly array"); + } else if (_.isMap(target)) { + return createProxyWithBannedMethods(target, ['clear', 'delete', 'set'], "Cannot modify a readonly Map"); + } else if (_.isSet(target)) { + return createProxyWithBannedMethods(target, ['add', 'clear', 'delete'], "Cannot modify a readonly Set"); + } else if (_.isObject(target)) { return new Proxy(target, { get: function(o, prop) { if (prop === readOnlySymbol) { return true; } return createReadonlyProxy(o[prop]); }, - set: function(o, prop, value) { - return true; - }, - deleteProperty: function(o, prop) { - return true; - } + set: dummy, + deleteProperty: dummy }); } return target; -- GitLab