2025-09-05 14:59:21 +08:00

153 lines
3.4 KiB
JavaScript

import path from './path.js';
/**
@class Cache
@private
*/
export default class Cache {
constructor(equals) {
this._equals = equals;
this._proxyCache = new WeakMap();
this._pathCache = new WeakMap();
this.isUnsubscribed = false;
}
_getDescriptorCache() {
if (this._descriptorCache === undefined) {
this._descriptorCache = new WeakMap();
}
return this._descriptorCache;
}
_getProperties(target) {
const descriptorCache = this._getDescriptorCache();
let properties = descriptorCache.get(target);
if (properties === undefined) {
properties = {};
descriptorCache.set(target, properties);
}
return properties;
}
_getOwnPropertyDescriptor(target, property) {
if (this.isUnsubscribed) {
return Reflect.getOwnPropertyDescriptor(target, property);
}
const properties = this._getProperties(target);
let descriptor = properties[property];
if (descriptor === undefined) {
descriptor = Reflect.getOwnPropertyDescriptor(target, property);
properties[property] = descriptor;
}
return descriptor;
}
getProxy(target, path, handler, proxyTarget) {
if (this.isUnsubscribed) {
return target;
}
const reflectTarget = target[proxyTarget];
const source = reflectTarget ?? target;
this._pathCache.set(source, path);
let proxy = this._proxyCache.get(source);
if (proxy === undefined) {
proxy = reflectTarget === undefined
? new Proxy(target, handler)
: target;
this._proxyCache.set(source, proxy);
}
return proxy;
}
getPath(target) {
return this.isUnsubscribed ? undefined : this._pathCache.get(target);
}
isDetached(target, object) {
return !Object.is(target, path.get(object, this.getPath(target)));
}
defineProperty(target, property, descriptor) {
if (!Reflect.defineProperty(target, property, descriptor)) {
return false;
}
if (!this.isUnsubscribed) {
this._getProperties(target)[property] = descriptor;
}
return true;
}
setProperty(target, property, value, receiver, previous) { // eslint-disable-line max-params
if (!this._equals(previous, value) || !(property in target)) {
const descriptor = this._getOwnPropertyDescriptor(target, property);
if (descriptor !== undefined && 'set' in descriptor) {
return Reflect.set(target, property, value, receiver);
}
return Reflect.set(target, property, value);
}
return true;
}
deleteProperty(target, property, previous) {
if (Reflect.deleteProperty(target, property)) {
if (!this.isUnsubscribed) {
const properties = this._getDescriptorCache().get(target);
if (properties) {
delete properties[property];
this._pathCache.delete(previous);
}
}
return true;
}
return false;
}
isSameDescriptor(a, target, property) {
const b = this._getOwnPropertyDescriptor(target, property);
return a !== undefined
&& b !== undefined
&& Object.is(a.value, b.value)
&& (a.writable || false) === (b.writable || false)
&& (a.enumerable || false) === (b.enumerable || false)
&& (a.configurable || false) === (b.configurable || false)
&& a.get === b.get
&& a.set === b.set;
}
isGetInvariant(target, property) {
const descriptor = this._getOwnPropertyDescriptor(target, property);
return descriptor !== undefined
&& descriptor.configurable !== true
&& descriptor.writable !== true;
}
unsubscribe() {
this._descriptorCache = null;
this._pathCache = null;
this._proxyCache = null;
this.isUnsubscribed = true;
}
}