@samitouri / QOS-React-2 / commits / 896ef251b4

patch makeReadOnly into playground

Mofei Zhang committed Oct 3, 2022 at 14:37 UTC 896ef251b4f2c802313bb7019e49aaf004c8c4e6
1 file changed +102
compiler/forget/packages/playground/lib/patchUseMemoCache.ts
+102
@@ -28,6 +28,108 @@ export default function patchUseMemoCache(reactSource: string) {
28 return value;
29 };
30
31 + // Utility functions
32 + function isWriteable(desc) {
33 + return (desc.writable || desc.set) && desc.configurable;
34 + }
35 + function getOrInsertDefault(m, k) {
36 + const entry = m.get(k);
37 + if (entry) {
38 + return { existed: true, entry };
39 + }
40 + else {
41 + const newEntry = new Map();
42 + m.set(k, newEntry);
43 + return { existed: false, entry: newEntry };
44 + }
45 + }
46 + function buildMakeReadOnly(logger, skippedClasses, isInROMode) {
47 + // All saved proxys
48 + const savedROObjects = new WeakMap();
49 + // Overwrites an object property with its proxy and saves its original value
50 + function addProperty(obj, key, prop, savedEntries) {
51 + const proxy = {
52 + get() {
53 + // read from backing cache entry
54 + return makeReadOnly(savedEntries.get(key).savedVal);
55 + },
56 + set(newVal) {
57 + if (isInROMode()) {
58 + logger("FORGET_MUTATE_IMMUT", key, newVal);
59 + }
60 + // update backing cache entry
61 + savedEntries.get(key).savedVal = newVal;
62 + },
63 + };
64 + if (prop.configurable != null) {
65 + proxy.configurable = prop.configurable;
66 + }
67 + if (prop.enumerable != null) {
68 + proxy.enumerable = prop.enumerable;
69 + }
70 + savedEntries.set(key, { savedVal: obj[key], getter: proxy.get });
71 + Object.defineProperty(obj, key, proxy);
72 + }
73 + // Changes an object to be read-only, returns its input
74 + function makeReadOnly(o) {
75 + if (typeof o !== "object" || o == null) {
76 + return o;
77 + }
78 + else if (skippedClasses.includes(o.constructor.name)) {
79 + return o;
80 + }
81 + const { existed, entry: cache } = getOrInsertDefault(savedROObjects, o);
82 + for (const [k, entry] of cache.entries()) {
83 + const currentProp = Object.getOwnPropertyDescriptor(o, k);
84 + if (currentProp && !isWriteable(currentProp)) {
85 + continue;
86 + }
87 + const currentPropGetter = currentProp?.get;
88 + const cachedGetter = entry.getter;
89 + if (currentPropGetter !== cachedGetter) {
90 + // cache is currently holding an old property
91 + // - it may have been deleted
92 + // - it may have been deleted + re-set
93 + // (meaning that new value is not proxied,
94 + // and the current proxied value is stale)
95 + cache.delete(k);
96 + if (!currentProp && isInROMode()) {
97 + logger("FORGET_DELETE_PROP_IMMUT", k);
98 + }
99 + else if (currentProp) {
100 + if (isInROMode()) {
101 + logger("FORGET_CHANGE_PROP_IMMUT", k);
102 + }
103 + addProperty(o, k, currentProp, cache);
104 + }
105 + }
106 + }
107 + for (const [k, prop] of Object.entries(Object.getOwnPropertyDescriptors(o))) {
108 + if (!cache.has(k) && isWriteable(prop)) {
109 + if (isInROMode() && existed) {
110 + logger("FORGET_ADD_PROP_IMMUT", k);
111 + }
112 + addProperty(o, k, prop, cache);
113 + }
114 + }
115 + return o;
116 + }
117 + return makeReadOnly;
118 + }
119 +
120 + useMemoCache.makeReadOnly = buildMakeReadOnly(
121 + console.log,
122 + [
123 + // some immutVals have a transitive reference to global (e.g. LexicalEditor)
124 + 'Window',
125 + 'Storage', // useBrowserStorage
126 + // some immutVals (e.g. ref.current) have a ref to DOM nodes (e.g. BaseDOMContainer)
127 + // - since FiberNodes -should- be interior mutable, this is safe
128 + 'FiberNode',
129 + ],
130 + () => true, // always read-only
131 + );
132 +
133 React.useMemoCache = useMemoCache;
134
135 export default React;