mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
patch makeReadOnly into playground
This commit is contained in:
@@ -28,6 +28,108 @@ export default function patchUseMemoCache(reactSource: string) {
|
||||
return value;
|
||||
};
|
||||
|
||||
// Utility functions
|
||||
function isWriteable(desc) {
|
||||
return (desc.writable || desc.set) && desc.configurable;
|
||||
}
|
||||
function getOrInsertDefault(m, k) {
|
||||
const entry = m.get(k);
|
||||
if (entry) {
|
||||
return { existed: true, entry };
|
||||
}
|
||||
else {
|
||||
const newEntry = new Map();
|
||||
m.set(k, newEntry);
|
||||
return { existed: false, entry: newEntry };
|
||||
}
|
||||
}
|
||||
function buildMakeReadOnly(logger, skippedClasses, isInROMode) {
|
||||
// All saved proxys
|
||||
const savedROObjects = new WeakMap();
|
||||
// Overwrites an object property with its proxy and saves its original value
|
||||
function addProperty(obj, key, prop, savedEntries) {
|
||||
const proxy = {
|
||||
get() {
|
||||
// read from backing cache entry
|
||||
return makeReadOnly(savedEntries.get(key).savedVal);
|
||||
},
|
||||
set(newVal) {
|
||||
if (isInROMode()) {
|
||||
logger("FORGET_MUTATE_IMMUT", key, newVal);
|
||||
}
|
||||
// update backing cache entry
|
||||
savedEntries.get(key).savedVal = newVal;
|
||||
},
|
||||
};
|
||||
if (prop.configurable != null) {
|
||||
proxy.configurable = prop.configurable;
|
||||
}
|
||||
if (prop.enumerable != null) {
|
||||
proxy.enumerable = prop.enumerable;
|
||||
}
|
||||
savedEntries.set(key, { savedVal: obj[key], getter: proxy.get });
|
||||
Object.defineProperty(obj, key, proxy);
|
||||
}
|
||||
// Changes an object to be read-only, returns its input
|
||||
function makeReadOnly(o) {
|
||||
if (typeof o !== "object" || o == null) {
|
||||
return o;
|
||||
}
|
||||
else if (skippedClasses.includes(o.constructor.name)) {
|
||||
return o;
|
||||
}
|
||||
const { existed, entry: cache } = getOrInsertDefault(savedROObjects, o);
|
||||
for (const [k, entry] of cache.entries()) {
|
||||
const currentProp = Object.getOwnPropertyDescriptor(o, k);
|
||||
if (currentProp && !isWriteable(currentProp)) {
|
||||
continue;
|
||||
}
|
||||
const currentPropGetter = currentProp?.get;
|
||||
const cachedGetter = entry.getter;
|
||||
if (currentPropGetter !== cachedGetter) {
|
||||
// cache is currently holding an old property
|
||||
// - it may have been deleted
|
||||
// - it may have been deleted + re-set
|
||||
// (meaning that new value is not proxied,
|
||||
// and the current proxied value is stale)
|
||||
cache.delete(k);
|
||||
if (!currentProp && isInROMode()) {
|
||||
logger("FORGET_DELETE_PROP_IMMUT", k);
|
||||
}
|
||||
else if (currentProp) {
|
||||
if (isInROMode()) {
|
||||
logger("FORGET_CHANGE_PROP_IMMUT", k);
|
||||
}
|
||||
addProperty(o, k, currentProp, cache);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (const [k, prop] of Object.entries(Object.getOwnPropertyDescriptors(o))) {
|
||||
if (!cache.has(k) && isWriteable(prop)) {
|
||||
if (isInROMode() && existed) {
|
||||
logger("FORGET_ADD_PROP_IMMUT", k);
|
||||
}
|
||||
addProperty(o, k, prop, cache);
|
||||
}
|
||||
}
|
||||
return o;
|
||||
}
|
||||
return makeReadOnly;
|
||||
}
|
||||
|
||||
useMemoCache.makeReadOnly = buildMakeReadOnly(
|
||||
console.log,
|
||||
[
|
||||
// some immutVals have a transitive reference to global (e.g. LexicalEditor)
|
||||
'Window',
|
||||
'Storage', // useBrowserStorage
|
||||
// some immutVals (e.g. ref.current) have a ref to DOM nodes (e.g. BaseDOMContainer)
|
||||
// - since FiberNodes -should- be interior mutable, this is safe
|
||||
'FiberNode',
|
||||
],
|
||||
() => true, // always read-only
|
||||
);
|
||||
|
||||
React.useMemoCache = useMemoCache;
|
||||
|
||||
export default React;
|
||||
|
||||
Reference in New Issue
Block a user