| 51 | } |
| 52 | |
| 53 | function buildMakeReadOnly( |
| 54 | logger: ROViolationLogger, |
| 55 | skippedClasses: string[], |
| 56 | ): <T>(val: T, source: string) => T { |
| 57 | // All saved proxys |
| 58 | const savedROObjects: SavedROObjects = new WeakMap(); |
| 59 | |
| 60 | // Overwrites an object property with its proxy and saves its original value |
| 61 | function addProperty( |
| 62 | obj: Object, |
| 63 | source: string, |
| 64 | key: string, |
| 65 | prop: PropertyDescriptor, |
| 66 | savedEntries: Map<string, SavedEntry>, |
| 67 | ) { |
| 68 | const proxy: PropertyDescriptor & {get(): unknown} = { |
| 69 | get() { |
| 70 | // read from backing cache entry |
| 71 | return makeReadOnly(savedEntries.get(key)!.savedVal, source); |
| 72 | }, |
| 73 | set(newVal: unknown) { |
| 74 | logger('FORGET_MUTATE_IMMUT', source, key, newVal); |
| 75 | // update backing cache entry |
| 76 | savedEntries.get(key)!.savedVal = newVal; |
| 77 | }, |
| 78 | }; |
| 79 | if (prop.configurable != null) { |
| 80 | proxy.configurable = prop.configurable; |
| 81 | } |
| 82 | if (prop.enumerable != null) { |
| 83 | proxy.enumerable = prop.enumerable; |
| 84 | } |
| 85 | |
| 86 | savedEntries.set(key, {savedVal: (obj as any)[key], getter: proxy.get}); |
| 87 | Object.defineProperty(obj, key, proxy); |
| 88 | } |
| 89 | |
| 90 | // Changes an object to be read-only, returns its input |
| 91 | function makeReadOnly<T>(o: T, source: string): T { |
| 92 | if (typeof o !== 'object' || o == null) { |
| 93 | return o; |
| 94 | } else if ( |
| 95 | o.constructor?.name != null && |
| 96 | skippedClasses.includes(o.constructor.name) |
| 97 | ) { |
| 98 | return o; |
| 99 | } |
| 100 | |
| 101 | const {existed, entry: cache} = getOrInsertDefault(savedROObjects, o); |
| 102 | |
| 103 | for (const [k, entry] of cache.entries()) { |
| 104 | const currentProp = Object.getOwnPropertyDescriptor(o, k); |
| 105 | if (currentProp && !isWriteable(currentProp)) { |
| 106 | continue; |
| 107 | } |
| 108 | const currentPropGetter = currentProp?.get; |
| 109 | const cachedGetter = entry.getter; |
| 110 | |