Skip to content

Commit

Permalink
fix(loader): fix isolate garbage collection
Browse files Browse the repository at this point in the history
  • Loading branch information
shigma committed May 1, 2024
1 parent d37e598 commit 25e595f
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 61 deletions.
42 changes: 28 additions & 14 deletions packages/loader/src/entry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export namespace Entry {
config?: any
disabled?: boolean | null
intercept?: Dict | null
isolate?: Dict<boolean | string>
isolate?: Dict<true | string>
when?: any
}
}
Expand Down Expand Up @@ -54,20 +54,30 @@ export class Entry {
if (index >= 0) config.splice(index, 1)
}

amend(ctx?: Context) {
resolveRealm(label: string | true) {
if (label === true) {
return '#' + this.options.id
} else {
return '@' + label
}
}

patch(ctx?: Context, legacy?: Entry.Options) {
ctx ??= this.parent.extend({
[Context.intercept]: Object.create(this.parent[Context.intercept]),
[Context.isolate]: Object.create(this.parent[Context.isolate]),
})
ctx.emit('loader/patch', this)
ctx.emit('loader/patch', this, legacy)
swap(ctx[Context.intercept], this.options.intercept)
const newMap: Dict<symbol> = Object.create(Object.getPrototypeOf(ctx[Context.isolate]))
for (const [key, label] of Object.entries(this.options.isolate ?? {})) {
if (typeof label === 'string') {
newMap[key] = (this.loader.realms[label] ??= Object.create(null))[key] ??= Symbol(`${key}@${label}`)
} else if (label) {
newMap[key] = Symbol(`${key}#${this.options.id}`)
}
const realm = this.resolveRealm(label)
newMap[key] = (this.loader.realms[realm] ??= Object.create(null))[key] ??= Symbol(`${key}${realm}`)
}
for (const key in legacy?.isolate ?? {}) {
if (this.options.isolate?.[key] === legacy!.isolate![key]) continue
const name = this.resolveRealm(legacy!.isolate![key])
this.loader._clearRealm(key, name)
}
const delimiter = Symbol('delimiter')
ctx[delimiter] = true
Expand Down Expand Up @@ -98,7 +108,7 @@ export class Entry {
}
swap(ctx[Context.isolate], newMap)
for (const [, symbol1, symbol2, flag] of diff) {
if (flag && ctx[symbol1]) {
if (flag && ctx[symbol1] && !ctx[symbol2]) {
ctx.root[symbol2] = ctx.root[symbol1]
delete ctx.root[symbol1]
}
Expand All @@ -116,21 +126,21 @@ export class Entry {
return ctx
}

// TODO: handle parent change
async update(parent: Context, options: Entry.Options) {
const legacy = this.options
this.parent = parent
this.options = sortKeys(options)
if (!this.loader.isTruthyLike(options.when) || options.disabled) {
this.stop()
} else if (this.fork) {
this.isUpdate = true
this.amend(this.fork.parent)
this.patch(this.fork.parent, legacy)
this.fork.update(this.options.config)
} else {
this.parent.emit('loader/entry', 'apply', this)
const plugin = await this.loader.resolve(this.options.name)
if (!plugin) return
const ctx = this.amend()
const ctx = this.patch()
this.fork = ctx.plugin(plugin, this.options.config)
this.fork.entry = this
}
Expand All @@ -139,7 +149,11 @@ export class Entry {
stop() {
this.fork?.dispose()
this.fork = undefined

// realm garbage collection
for (const [key, label] of Object.entries(this.options.isolate ?? {})) {
const name = this.resolveRealm(label)
this.loader._clearRealm(key, name)
}
}
}

Error.stackTraceLimit = 100
28 changes: 21 additions & 7 deletions packages/loader/src/shared.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ declare module '@cordisjs/core' {
'config'(): void
'exit'(signal: NodeJS.Signals): Promise<void>
'loader/entry'(type: string, entry: Entry): void
'loader/patch'(entry: Entry): void
'loader/patch'(entry: Entry, legacy?: Entry.Options): void
}

interface Context {
Expand Down Expand Up @@ -81,7 +81,7 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
super(app, 'loader', true)
this.root = new Entry(this)
this.entries[''] = this.root
this.realms.root = app.root[Context.isolate]
this.realms['#'] = app.root[Context.isolate]

this.app.on('dispose', () => {
this.exit()
Expand Down Expand Up @@ -241,18 +241,19 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
return id
}

async update(id: string, options: Entry.Options) {
async update(id: string, options: Partial<Omit<Entry.Options, 'id' | 'name'>>) {
const entry = this.entries[id]
if (!entry) throw new Error(`entry ${id} not found`)
const override = { ...entry.options }
for (const [key, value] of Object.entries(options)) {
if (isNullable(value)) {
delete entry.options[key]
delete override[key]
} else {
entry.options[key] = value
override[key] = value
}
}
this.writeConfig()
return entry.update(entry.parent, entry.options)
return entry.update(entry.parent, override)
}

async create(options: Omit<Entry.Options, 'id'>, target = '', index = Infinity) {
Expand Down Expand Up @@ -313,7 +314,6 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
name: 'cordis/group',
config: this.config,
})
this.app.emit('config')

while (this.tasks.size) {
await Promise.all(this.tasks)
Expand All @@ -325,6 +325,20 @@ export abstract class Loader<T extends Loader.Options = Loader.Options> extends
}

exit() {}

_clearRealm(key: string, name: string) {
const hasRef = Object.values(this.entries).some((entry) => {
if (!entry.fork) return false
const label = entry.options.isolate?.[key]
if (!label) return false
return name === entry.resolveRealm(label)
})
if (hasRef) return
delete this.realms[name][key]
if (!Object.keys(this.realms[name]).length) {
delete this.realms[name]
}
}
}

export const kGroup = Symbol.for('cordis.group')
Expand Down
Loading

0 comments on commit 25e595f

Please sign in to comment.