Skip to content

Commit

Permalink
inherit patch
Browse files Browse the repository at this point in the history
  • Loading branch information
eddow committed Nov 4, 2024
1 parent f6a3079 commit c2e9b7c
Show file tree
Hide file tree
Showing 6 changed files with 77 additions and 47 deletions.
6 changes: 3 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "flat-diamond",
"version": "1.0.7",
"version": "1.0.8",
"types": "./lib/index.d.ts",
"exports": {
".": {
Expand Down
33 changes: 21 additions & 12 deletions src/diamond.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,29 @@
import { Ctor, HasBases, Newable } from './types'
import { allFLegs, bottomLeg, emptySecludedProxyHandler, fLegs, nextInFLeg } from './utils'
import {
allFLegs,
bottomLeg,
emptySecludedProxyHandler,
fLegs,
hasInstanceManager,
hasInstanceManagers,
linearLeg,
manageHasInstance,
nextInFLeg
} from './utils'

type BuildingStrategy = Map<Ctor, Ctor[]>
let buildingDiamond: {
built: object
strategy: BuildingStrategy
} | null = null

export const diamondHandler: {
const diamondHandler: {
getPrototypeOf(target: Ctor): Ctor
get(target: Ctor, p: PropertyKey, receiver: Ctor): any
set(target: Ctor, p: PropertyKey, v: any, receiver: Ctor): boolean
} & ProxyHandler<Ctor> = {
get(target, p, receiver) {
if (p === 'constructor') return Object
const pd = nextInFLeg(receiver.constructor, p, target)
return pd && ('value' in pd ? pd.value : 'get' in pd ? pd.get!.call(receiver) : undefined)
},
Expand All @@ -34,16 +45,6 @@ export const diamondHandler: {
}
}

export function hasInstanceManager<Class extends Ctor>(cls: Class) {
return (obj: any) => {
if (!obj || typeof obj !== 'object') return false
const objBottom = bottomLeg(obj.constructor)
if (objBottom === cls) return true
const fLeg = allFLegs.get(objBottom)
return Boolean(fLeg && fLeg.some((base) => bottomLeg(base) === cls))
}
}

export default function Diamond<TBases extends Ctor[]>(
...baseClasses: TBases
): Newable<HasBases<TBases>> {
Expand Down Expand Up @@ -73,6 +74,10 @@ export default function Diamond<TBases extends Ctor[]>(
const responsibility = buildingDiamond
? buildingDiamond!.strategy.get(this.constructor as Ctor)!
: myResponsibility
if (!responsibility) {
throw new Error(`Inconsistent diamond hierarchy.
This might happen if a diamond is created from another constructor before its 'super(...)' is called.`)
}
if (!buildingDiamond)
buildingDiamond = {
built: this,
Expand Down Expand Up @@ -107,11 +112,15 @@ export default function Diamond<TBases extends Ctor[]>(
//In the constructor method and in the field initializers, we can build diamonds, but not *this* diamond
buildingDiamond = null
}
// Value used by `this` on `super(...)` return
return locallyStoredDiamond.built
}
static [Symbol.hasInstance] = hasInstanceManager(Diamond)
}
hasInstanceManagers.add(Diamond)
allFLegs.set(Diamond, bases)
for (const base of baseClasses)
if (!fLegs(base)) for (const ctor of linearLeg(base)) if (!manageHasInstance(ctor)) break
/**
* Constructs the building strategy for building this class and only this class with its specific legacy
*/
Expand Down
14 changes: 3 additions & 11 deletions src/seclude.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import Diamond, { diamondHandler, hasInstanceManager } from './diamond'
import Diamond from './diamond'
import { Ctor, KeySet, Newable } from './types'
import {
allFLegs,
Expand Down Expand Up @@ -59,13 +59,6 @@ export function Seclude<TBase extends Ctor, Keys extends (keyof InstanceType<TBa
const privates = new WeakMap<GateKeeper, TBase>(),
diamondSecluded = !fLegs(base),
diamond = diamondSecluded ? Diamond(PropertyCollector) : PropertyCollector
// We make sure `Secluded(X).secluded(x) instanceof X`
if (diamondSecluded) {
Object.defineProperty(base, Symbol.hasInstance, {
value: hasInstanceManager(base),
configurable: true
})
}
class GateKeeper extends diamond {
static secluded(obj: TBase): TBase | undefined {
return privates.get(obj)
Expand Down Expand Up @@ -178,7 +171,7 @@ export function Seclude<TBase extends Ctor, Keys extends (keyof InstanceType<TBa
if (p in secludedProperties && actor.domain === 'private')
// If we arrive here, it means it's private but not set in the private part
return undefined
if (allFLegs.has(actor.public)) return diamondHandler.get(bottomLeg(target), p, receiver)
if (allFLegs.has(actor.public)) return Reflect.get(bottomLeg(target), p, receiver)
// If we arrive here, it means it's public but not set in the public part
return undefined
},
Expand All @@ -202,8 +195,7 @@ export function Seclude<TBase extends Ctor, Keys extends (keyof InstanceType<TBa
})
return true
}
if (allFLegs.has(actor.public))
return diamondHandler.set(bottomLeg(target), p, value, receiver)
if (allFLegs.has(actor.public)) return Reflect.set(bottomLeg(target), p, value, receiver)
Object.defineProperty(actor.public, p, {
value,
writable: true,
Expand Down
34 changes: 34 additions & 0 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -99,3 +99,37 @@ export function secludedProxyHandler<TBase extends Ctor>(
} as ProxyHandler<Ctor>
}
export const emptySecludedProxyHandler = secludedProxyHandler(null, {})

export function hasInstanceManager<Class extends Ctor>(
cls: Class,
original?: (obj: any) => boolean
) {
function nativeLinear(ctor: Ctor) {
// linearLeg ignore last diamond (that we need)
for (; ctor !== Object; ctor = Object.getPrototypeOf(ctor.prototype).constructor)
if (ctor === cls) return true
return false
}
const inheritsFrom = original
? (ctor: Ctor) => nativeLinear(ctor) || original(ctor.prototype)
: (ctor: Ctor) => nativeLinear(ctor)
return (obj: any) => {
if (!obj || typeof obj !== 'object') return false
if (inheritsFrom(obj.constructor)) return true
const fLeg = fLegs(obj.constructor)
return Boolean(fLeg && fLeg.some(inheritsFrom))
}
}
export const hasInstanceManagers = new WeakSet<Ctor>()
export function manageHasInstance(ctor: Ctor) {
if (hasInstanceManagers.has(ctor)) return false
hasInstanceManagers.add(ctor)
Object.defineProperty(ctor, Symbol.hasInstance, {
value: hasInstanceManager(
ctor,
ctor.hasOwnProperty(Symbol.hasInstance) ? ctor[Symbol.hasInstance] : undefined
),
configurable: true
})
return true
}
35 changes: 15 additions & 20 deletions test/instanceOf.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import D, { instanceOf } from '../src'
import D, { instanceOf, Seclude } from '../src'
class A1 {}
class A2 extends A1 {}
class B1 {}
Expand All @@ -13,34 +13,29 @@ class X2 extends D(C2, D2) {}

const x1 = new X1(),
x2 = new X2()
test('instanceOf', () => {
expect(instanceOf(x1, X1)).toBe(true)
expect(instanceOf(x1, X2)).toBe(false)
expect(instanceOf(x2, X1)).toBe(false)
expect(instanceOf(x2, X2)).toBe(true)

expect(instanceOf(x1, A1)).toBe(true)
expect(instanceOf(x1, B1)).toBe(true)
expect(instanceOf(x1, C1)).toBe(false)

expect(instanceOf(x2, A1)).toBe(true)
expect(instanceOf(x2, B1)).toBe(true)
expect(instanceOf(x2, A2)).toBe(false)
expect(instanceOf(x2, B2)).toBe(false)
expect(instanceOf(x2, C1)).toBe(true)
})
test('instanceof', () => {
/*
expect(x1 instanceof X1).toBe(true)
expect(x1 instanceof X2).toBe(false)
expect(x2 instanceof X1).toBe(false)
expect(x2 instanceof X2).toBe(true)
expect(x1 instanceof A1).toBe(false)
expect(x1 instanceof B1).toBe(false)
expect(x1 instanceof A1).toBe(true)
expect(x1 instanceof B1).toBe(true)
expect(x1 instanceof C1).toBe(false)
expect(x2 instanceof A1).toBe(false)
expect(x2 instanceof B1).toBe(false)
expect(x2 instanceof A1).toBe(true)
expect(x2 instanceof B1).toBe(true)*/
expect(x2 instanceof A2).toBe(false)
expect(x2 instanceof B2).toBe(false)
expect(x2 instanceof C1).toBe(true)
})

test('secluded', () => {
class X {}
const S = Seclude(X)
const s = new S()
expect(s instanceof X).toBe(true)
expect(s instanceof S).toBe(true)
expect(S.secluded(s) instanceof X).toBe(true)
})

0 comments on commit c2e9b7c

Please sign in to comment.