362 lines
16 KiB
Plaintext
362 lines
16 KiB
Plaintext
"use strict";
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
exports.PlanResultCacheService = void 0;
|
|
const BindingType_1 = require("../../binding/models/BindingType");
|
|
const WeakList_1 = require("../../common/models/WeakList");
|
|
const addRootServiceNodeBindingIfContextFree_1 = require("../actions/addRootServiceNodeBindingIfContextFree");
|
|
const addServiceNodeBindingIfContextFree_1 = require("../actions/addServiceNodeBindingIfContextFree");
|
|
const removeRootServiceNodeBindingIfContextFree_1 = require("../actions/removeRootServiceNodeBindingIfContextFree");
|
|
const removeServiceNodeBindingIfContextFree_1 = require("../actions/removeServiceNodeBindingIfContextFree");
|
|
const CacheBindingInvalidationKind_1 = require("../models/CacheBindingInvalidationKind");
|
|
const LazyPlanServiceNode_1 = require("../models/LazyPlanServiceNode");
|
|
const CHAINED_MASK = 0x4;
|
|
const IS_MULTIPLE_MASK = 0x2;
|
|
const OPTIONAL_MASK = 0x1;
|
|
const MAP_ARRAY_LENGTH = 0x8;
|
|
/**
|
|
* Service to cache plans.
|
|
*
|
|
* This class is used to cache plans and to notify PlanService subscribers when the cache is cleared.
|
|
* The cache should be cleared when a new binding is registered or when a binding is unregistered.
|
|
*
|
|
* Subscribers are supposed to be plan services from child containers.
|
|
*
|
|
* Ancestor binding constraints are the reason to avoid reusing plans from plan children nodes.
|
|
*/
|
|
class PlanResultCacheService {
|
|
#serviceIdToNonCachedServiceNodeMapMap;
|
|
#serviceIdToValuePlanMap;
|
|
#namedServiceIdToValuePlanMap;
|
|
#namedTaggedServiceIdToValuePlanMap;
|
|
#taggedServiceIdToValuePlanMap;
|
|
#subscribers;
|
|
constructor() {
|
|
this.#serviceIdToNonCachedServiceNodeMapMap = new Map();
|
|
this.#serviceIdToValuePlanMap = this.#buildInitializedMapArray();
|
|
this.#namedServiceIdToValuePlanMap = this.#buildInitializedMapArray();
|
|
this.#namedTaggedServiceIdToValuePlanMap = this.#buildInitializedMapArray();
|
|
this.#taggedServiceIdToValuePlanMap = this.#buildInitializedMapArray();
|
|
this.#subscribers = new WeakList_1.WeakList();
|
|
}
|
|
clearCache() {
|
|
for (const map of this.#getMaps()) {
|
|
map.clear();
|
|
}
|
|
for (const subscriber of this.#subscribers) {
|
|
subscriber.clearCache();
|
|
}
|
|
}
|
|
get(options) {
|
|
if (options.name === undefined) {
|
|
if (options.tag === undefined) {
|
|
return this.#getMapFromMapArray(this.#serviceIdToValuePlanMap, options).get(options.serviceIdentifier);
|
|
}
|
|
else {
|
|
return this.#getMapFromMapArray(this.#taggedServiceIdToValuePlanMap, options)
|
|
.get(options.serviceIdentifier)
|
|
?.get(options.tag.key)
|
|
?.get(options.tag.value);
|
|
}
|
|
}
|
|
else {
|
|
if (options.tag === undefined) {
|
|
return this.#getMapFromMapArray(this.#namedServiceIdToValuePlanMap, options)
|
|
.get(options.serviceIdentifier)
|
|
?.get(options.name);
|
|
}
|
|
else {
|
|
return this.#getMapFromMapArray(this.#namedTaggedServiceIdToValuePlanMap, options)
|
|
.get(options.serviceIdentifier)
|
|
?.get(options.name)
|
|
?.get(options.tag.key)
|
|
?.get(options.tag.value);
|
|
}
|
|
}
|
|
}
|
|
invalidateServiceBinding(invalidation) {
|
|
this.#invalidateServiceMap(invalidation);
|
|
this.#invalidateNamedServiceMap(invalidation);
|
|
this.#invalidateNamedTaggedServiceMap(invalidation);
|
|
this.#invalidateTaggedServiceMap(invalidation);
|
|
this.#invalidateNonCachedServiceNodeSetMap(invalidation);
|
|
for (const subscriber of this.#subscribers) {
|
|
subscriber.invalidateServiceBinding(invalidation);
|
|
}
|
|
}
|
|
set(options, planResult) {
|
|
if (options.name === undefined) {
|
|
if (options.tag === undefined) {
|
|
this.#getMapFromMapArray(this.#serviceIdToValuePlanMap, options).set(options.serviceIdentifier, planResult);
|
|
}
|
|
else {
|
|
this.#getOrBuildMapValueFromMapMap(this.#getOrBuildMapValueFromMapMap(this.#getMapFromMapArray(this.#taggedServiceIdToValuePlanMap, options), options.serviceIdentifier), options.tag.key).set(options.tag.value, planResult);
|
|
}
|
|
}
|
|
else {
|
|
if (options.tag === undefined) {
|
|
this.#getOrBuildMapValueFromMapMap(this.#getMapFromMapArray(this.#namedServiceIdToValuePlanMap, options), options.serviceIdentifier).set(options.name, planResult);
|
|
}
|
|
else {
|
|
this.#getOrBuildMapValueFromMapMap(this.#getOrBuildMapValueFromMapMap(this.#getOrBuildMapValueFromMapMap(this.#getMapFromMapArray(this.#namedTaggedServiceIdToValuePlanMap, options), options.serviceIdentifier), options.name), options.tag.key).set(options.tag.value, planResult);
|
|
}
|
|
}
|
|
}
|
|
setNonCachedServiceNode(node, context) {
|
|
let nonCachedMap = this.#serviceIdToNonCachedServiceNodeMapMap.get(node.serviceIdentifier);
|
|
if (nonCachedMap === undefined) {
|
|
nonCachedMap = new Map();
|
|
this.#serviceIdToNonCachedServiceNodeMapMap.set(node.serviceIdentifier, nonCachedMap);
|
|
}
|
|
nonCachedMap.set(node, context);
|
|
}
|
|
subscribe(subscriber) {
|
|
this.#subscribers.push(subscriber);
|
|
}
|
|
#buildInitializedMapArray() {
|
|
const mapArray = new Array(MAP_ARRAY_LENGTH);
|
|
for (let i = 0; i < mapArray.length; ++i) {
|
|
mapArray[i] = new Map();
|
|
}
|
|
return mapArray;
|
|
}
|
|
#buildUpdatePlanParams(invalidation, index, name, tag) {
|
|
const isMultiple = (index & IS_MULTIPLE_MASK) !== 0;
|
|
let planParamsConstraint;
|
|
if (isMultiple) {
|
|
const isChained = (index & IS_MULTIPLE_MASK & CHAINED_MASK) !== 0;
|
|
planParamsConstraint = {
|
|
chained: isChained,
|
|
isMultiple,
|
|
serviceIdentifier: invalidation.binding.serviceIdentifier,
|
|
};
|
|
}
|
|
else {
|
|
planParamsConstraint = {
|
|
isMultiple,
|
|
serviceIdentifier: invalidation.binding.serviceIdentifier,
|
|
};
|
|
}
|
|
const isOptional = (index & OPTIONAL_MASK) !== 0;
|
|
if (isOptional) {
|
|
planParamsConstraint.isOptional = true;
|
|
}
|
|
if (name !== undefined) {
|
|
planParamsConstraint.name = name;
|
|
}
|
|
if (tag !== undefined) {
|
|
planParamsConstraint.tag = tag;
|
|
}
|
|
return {
|
|
autobindOptions: undefined,
|
|
operations: invalidation.operations,
|
|
rootConstraints: planParamsConstraint,
|
|
servicesBranch: [],
|
|
};
|
|
}
|
|
#getOrBuildMapValueFromMapMap(map, key) {
|
|
let valueMap = map.get(key);
|
|
if (valueMap === undefined) {
|
|
valueMap = new Map();
|
|
map.set(key, valueMap);
|
|
}
|
|
return valueMap;
|
|
}
|
|
#getMapFromMapArray(mapArray, options) {
|
|
return mapArray[this.#getMapArrayIndex(options)];
|
|
}
|
|
#getMaps() {
|
|
return [
|
|
this.#serviceIdToNonCachedServiceNodeMapMap,
|
|
...this.#serviceIdToValuePlanMap,
|
|
...this.#namedServiceIdToValuePlanMap,
|
|
...this.#namedTaggedServiceIdToValuePlanMap,
|
|
...this.#taggedServiceIdToValuePlanMap,
|
|
];
|
|
}
|
|
#getMapArrayIndex(options) {
|
|
if (options.isMultiple) {
|
|
return ((options.chained ? CHAINED_MASK : 0) |
|
|
(options.optional ? OPTIONAL_MASK : 0) |
|
|
IS_MULTIPLE_MASK);
|
|
}
|
|
else {
|
|
return options.optional ? OPTIONAL_MASK : 0;
|
|
}
|
|
}
|
|
#invalidateNamedServiceMap(invalidation) {
|
|
for (const [index, map] of this.#namedServiceIdToValuePlanMap.entries()) {
|
|
const servicePlans = map.get(invalidation.binding.serviceIdentifier);
|
|
if (servicePlans !== undefined) {
|
|
for (const [name, servicePlan] of servicePlans.entries()) {
|
|
this.#updatePlan(invalidation, servicePlan, index, name, undefined);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#invalidateNamedTaggedServiceMap(invalidation) {
|
|
for (const [index, map,] of this.#namedTaggedServiceIdToValuePlanMap.entries()) {
|
|
const servicePlanMapMapMap = map.get(invalidation.binding.serviceIdentifier);
|
|
if (servicePlanMapMapMap !== undefined) {
|
|
for (const [name, servicePlanMapMap,] of servicePlanMapMapMap.entries()) {
|
|
for (const [tag, servicePlanMap] of servicePlanMapMap.entries()) {
|
|
for (const [tagValue, servicePlan] of servicePlanMap.entries()) {
|
|
this.#updatePlan(invalidation, servicePlan, index, name, {
|
|
key: tag,
|
|
value: tagValue,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#invalidateNonCachePlanBindingNodeDescendents(planBindingNode) {
|
|
switch (planBindingNode.binding.type) {
|
|
case BindingType_1.bindingTypeValues.ServiceRedirection:
|
|
for (const redirection of planBindingNode.redirections) {
|
|
this.#invalidateNonCachePlanBindingNodeDescendents(redirection);
|
|
}
|
|
break;
|
|
case BindingType_1.bindingTypeValues.Instance:
|
|
for (const constructorParam of planBindingNode
|
|
.constructorParams) {
|
|
if (constructorParam !== undefined) {
|
|
this.#invalidateNonCachePlanServiceNode(constructorParam);
|
|
}
|
|
}
|
|
for (const propertyParam of planBindingNode.propertyParams.values()) {
|
|
this.#invalidateNonCachePlanServiceNode(propertyParam);
|
|
}
|
|
break;
|
|
case BindingType_1.bindingTypeValues.ResolvedValue:
|
|
for (const resolvedValue of planBindingNode.params) {
|
|
this.#invalidateNonCachePlanServiceNode(resolvedValue);
|
|
}
|
|
break;
|
|
default:
|
|
}
|
|
}
|
|
#invalidateNonCachePlanServiceNode(planServiceNode) {
|
|
const serviceNonCachedMap = this.#serviceIdToNonCachedServiceNodeMapMap.get(planServiceNode.serviceIdentifier);
|
|
if (serviceNonCachedMap === undefined ||
|
|
!serviceNonCachedMap.has(planServiceNode)) {
|
|
return;
|
|
}
|
|
serviceNonCachedMap.delete(planServiceNode);
|
|
this.#invalidateNonCachePlanServiceNodeDescendents(planServiceNode);
|
|
}
|
|
#invalidateNonCachePlanServiceNodeDescendents(planServiceNode) {
|
|
if (LazyPlanServiceNode_1.LazyPlanServiceNode.is(planServiceNode) &&
|
|
!planServiceNode.isExpanded()) {
|
|
return;
|
|
}
|
|
if (planServiceNode.bindings === undefined) {
|
|
return;
|
|
}
|
|
if (Array.isArray(planServiceNode.bindings)) {
|
|
for (const binding of planServiceNode.bindings) {
|
|
this.#invalidateNonCachePlanBindingNodeDescendents(binding);
|
|
}
|
|
}
|
|
else {
|
|
this.#invalidateNonCachePlanBindingNodeDescendents(planServiceNode.bindings);
|
|
}
|
|
}
|
|
#invalidateNonCachedServiceNodeSetMap(invalidation) {
|
|
const serviceNonCachedServiceNodeMap = this.#serviceIdToNonCachedServiceNodeMapMap.get(invalidation.binding.serviceIdentifier);
|
|
if (serviceNonCachedServiceNodeMap !== undefined) {
|
|
switch (invalidation.kind) {
|
|
case CacheBindingInvalidationKind_1.CacheBindingInvalidationKind.bindingAdded:
|
|
for (const [serviceNode, context] of serviceNonCachedServiceNodeMap) {
|
|
const result = (0, addServiceNodeBindingIfContextFree_1.addServiceNodeBindingIfContextFree)({
|
|
autobindOptions: undefined,
|
|
operations: invalidation.operations,
|
|
servicesBranch: [],
|
|
}, serviceNode, invalidation.binding, context.bindingConstraintsList, context.chainedBindings);
|
|
if (result.isContextFreeBinding) {
|
|
if (result.shouldInvalidateServiceNode &&
|
|
LazyPlanServiceNode_1.LazyPlanServiceNode.is(serviceNode)) {
|
|
this.#invalidateNonCachePlanServiceNodeDescendents(serviceNode);
|
|
serviceNode.invalidate();
|
|
}
|
|
}
|
|
else {
|
|
this.clearCache();
|
|
}
|
|
}
|
|
break;
|
|
case CacheBindingInvalidationKind_1.CacheBindingInvalidationKind.bindingRemoved:
|
|
for (const [serviceNode, context] of serviceNonCachedServiceNodeMap) {
|
|
const result = (0, removeServiceNodeBindingIfContextFree_1.removeServiceNodeBindingIfContextFree)(serviceNode, invalidation.binding, context.bindingConstraintsList, context.optionalBindings);
|
|
if (result.isContextFreeBinding) {
|
|
if (result.bindingNodeRemoved !== undefined) {
|
|
this.#invalidateNonCachePlanBindingNodeDescendents(result.bindingNodeRemoved);
|
|
}
|
|
}
|
|
else {
|
|
this.clearCache();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
#invalidateServiceMap(invalidation) {
|
|
for (const [index, map] of this.#serviceIdToValuePlanMap.entries()) {
|
|
const servicePlan = map.get(invalidation.binding.serviceIdentifier);
|
|
this.#updatePlan(invalidation, servicePlan, index, undefined, undefined);
|
|
}
|
|
}
|
|
#invalidateTaggedServiceMap(invalidation) {
|
|
for (const [index, map] of this.#taggedServiceIdToValuePlanMap.entries()) {
|
|
const servicePlanMapMap = map.get(invalidation.binding.serviceIdentifier);
|
|
if (servicePlanMapMap !== undefined) {
|
|
for (const [tag, servicePlanMap] of servicePlanMapMap.entries()) {
|
|
for (const [tagValue, servicePlan] of servicePlanMap.entries()) {
|
|
this.#updatePlan(invalidation, servicePlan, index, undefined, {
|
|
key: tag,
|
|
value: tagValue,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#updatePlan(invalidation, servicePlan, index, name, tag) {
|
|
if (servicePlan !== undefined &&
|
|
LazyPlanServiceNode_1.LazyPlanServiceNode.is(servicePlan.tree.root)) {
|
|
const planParams = this.#buildUpdatePlanParams(invalidation, index, name, tag);
|
|
switch (invalidation.kind) {
|
|
case CacheBindingInvalidationKind_1.CacheBindingInvalidationKind.bindingAdded:
|
|
{
|
|
const result = (0, addRootServiceNodeBindingIfContextFree_1.addRootServiceNodeBindingIfContextFree)(planParams, servicePlan.tree.root, invalidation.binding);
|
|
if (result.isContextFreeBinding) {
|
|
if (result.shouldInvalidateServiceNode) {
|
|
this.#invalidateNonCachePlanServiceNodeDescendents(servicePlan.tree.root);
|
|
servicePlan.tree.root.invalidate();
|
|
}
|
|
}
|
|
else {
|
|
this.clearCache();
|
|
}
|
|
}
|
|
break;
|
|
case CacheBindingInvalidationKind_1.CacheBindingInvalidationKind.bindingRemoved:
|
|
{
|
|
const result = (0, removeRootServiceNodeBindingIfContextFree_1.removeRootServiceNodeBindingIfContextFree)(planParams, servicePlan.tree.root, invalidation.binding);
|
|
if (result.isContextFreeBinding) {
|
|
if (result.bindingNodeRemoved !== undefined) {
|
|
this.#invalidateNonCachePlanBindingNodeDescendents(result.bindingNodeRemoved);
|
|
}
|
|
}
|
|
else {
|
|
this.clearCache();
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
exports.PlanResultCacheService = PlanResultCacheService;
|
|
//# sourceMappingURL=PlanResultCacheService.js.map |