##// END OF EJS Templates
WIP lifetime, service descriptors
WIP lifetime, service descriptors

File last commit:

r14:3f8a82c8ce73 default
r14:3f8a82c8ce73 default
Show More
ActivationContext.ts
153 lines | 4.8 KiB | video/mp2t | TypeScriptLexer
/ src / main / ts / ActivationContext.ts
import { ActivationError } from "./ActivationError";
import { Descriptor, ILifetime, IActivationContext, DescriptorMap, ILifetimeManager, ILifetimeSlot } from "./interfaces";
import { argumentNotNull, prototype } from "./traits";
export interface ActivationContextInfo {
name: string;
service: string;
}
let nextId = 1;
/** This object is created once per `Container.resolve` method call and used to
* cache dependencies and to track created instances. The activation context
* tracks services with `context` activation type.
*
* @template S The service map used in the activation context, services from
* this map are available to resolution.
* @template U A set of keys from the service map which can be overridden in
* this activation context.
*/
export class ActivationContext<S> implements IActivationContext<S> {
private readonly _cache: Record<string, unknown>;
private readonly _services: DescriptorMap<S>;
private readonly _name: string;
private readonly _service: Descriptor<S, unknown>;
private readonly _lifetimeManagers: ILifetimeManager[];
private readonly _parent: ActivationContext<S> | undefined;
/** Creates a new activation context with the specified parameters.
* @param containerLifetimeManager the container which starts the activation process
* @param services the initial service registrations
* @param name the name of the service being activated, this parameter is
* used for the debug purpose.
* @param service the service to activate, this parameter is used for the
* debug purpose.
*/
constructor(lifetimeManagers: ILifetimeManager[], services: DescriptorMap<S>, name: string, service: Descriptor<S, unknown>, cache = {}) {
this._name = name;
this._service = service;
this._cache = cache;
this._services = services;
this._lifetimeManagers = lifetimeManagers;
}
/** the name of the current resolving dependency */
getName() {
return this._name;
}
resolve<K extends keyof S>(name: K): NonNullable<S[K]>;
resolve<K extends keyof S, T>(name: K, def: T): NonNullable<S[K]> | T;
resolve<K extends keyof S, T>(name: K, def?: T): NonNullable<S[K]> | T | undefined {
const d = this._services[name];
if (d !== undefined) {
return this.activate(d, name.toString());
} else {
if (arguments.length > 1)
return def;
else
throw new Error(`Service ${String(name)} not found`);
}
}
/**
* registers services local to the the activation context
*
* @name{string} the name of the service
* @service{string} the service descriptor to register
*/
register<K extends keyof S>(name: K, service: DescriptorMap<S>[K]) {
argumentNotNull(name, "name");
const d = this._services[name];
if (d !== undefined && !d.configurable)
throw new Error(`Service ${String(name)} can't be overridden`);
this._services[name] = service;
}
ownerSlot<T>(slotId: string | number): ILifetimeSlot<T> {
return this._lifetimeManagers[this._service.level].slot(slotId);
}
containerSlot<T>(slotId: string | number): ILifetimeSlot<T> {
return this._lifetimeManagers[this._lifetimeManagers.length - 1].slot(slotId);
}
contextSlot<T>(slotId: string | number): ILifetimeSlot<T> {
}
createLifetime<T>(): ILifetime<T> {
const id = nextId++;
return {
initialize() { },
has: () => id in this._cache,
get: () => {
const v = this._cache[id] as T;
if (v === undefined || v === null)
throw new Error("The value isn't present in the activation context");
return v;
},
store: item => {
this._cache[id] = item;
}
};
}
activate<T>(d: Descriptor<S, T>, name: string) {
// TODO: add logging
// if (trace.isLogEnabled())
// trace.log("enter {0} {1}", name, d);
const ctx = new ActivationContext(
this._containerLifetimeManager,
d.hasOverrides ? prototype(this._services) : this._services,
name,
d,
this._cache
);
const v = d.activate(ctx);
// if (trace.isLogEnabled())
// trace.log(`leave ${name}`);
return v;
}
getStack(): ActivationContextInfo[] {
const stack = [{
name: this._name,
service: this._service.toString()
}];
return this._parent ?
stack.concat(this._parent.getStack()) :
stack;
}
fail(innerException: unknown): never {
throw new ActivationError(this._name, this.getStack(), innerException);
}
}