Skip to content

Commit

Permalink
Fix fallback handling, better tests, remove process.env reference
Browse files Browse the repository at this point in the history
  • Loading branch information
rbuckton committed Dec 9, 2023
1 parent 68761f1 commit 5a9d20a
Show file tree
Hide file tree
Showing 56 changed files with 1,479 additions and 2,390 deletions.
103 changes: 72 additions & 31 deletions Reflect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@ namespace Reflect {
declare const globalThis: any;
declare const crypto: Crypto;
declare const msCrypto: Crypto;
declare const process: any;

/**
* Applies a set of decorators to a target object.
Expand Down Expand Up @@ -563,20 +562,19 @@ namespace Reflect {
sloppyModeThis();

let exporter = makeExporter(Reflect);
if (typeof root.Reflect === "undefined") {
root.Reflect = Reflect;
}
else {
if (typeof root.Reflect !== "undefined") {
exporter = makeExporter(root.Reflect, exporter);
}

factory(exporter, root);

if (typeof root.Reflect === "undefined") {
root.Reflect = Reflect;
}

function makeExporter(target: typeof Reflect, previous?: <K extends keyof typeof Reflect>(key: K, value: typeof Reflect[K]) => void) {
return <K extends keyof typeof Reflect>(key: K, value: typeof Reflect[K]) => {
if (typeof target[key] !== "function") {
Object.defineProperty(target, key, { configurable: true, writable: true, value });
}
Object.defineProperty(target, key, { configurable: true, writable: true, value });
if (previous) previous(key, value);
};
}
Expand Down Expand Up @@ -623,10 +621,12 @@ namespace Reflect {

// Load global or shim versions of Map, Set, and WeakMap
const functionPrototype = Object.getPrototypeOf(Function);
const usePolyfill = typeof process === "object" && process["env" + ""] && process["env" + ""]["REFLECT_METADATA_USE_MAP_POLYFILL"] === "true";
const _Map: typeof Map = !usePolyfill && typeof Map === "function" && typeof Map.prototype.entries === "function" ? Map : CreateMapPolyfill();
const _Set: typeof Set = !usePolyfill && typeof Set === "function" && typeof Set.prototype.entries === "function" ? Set : CreateSetPolyfill();
const _WeakMap: typeof WeakMap = !usePolyfill && typeof WeakMap === "function" ? WeakMap : CreateWeakMapPolyfill();
const _Map: typeof Map = typeof Map === "function" && typeof Map.prototype.entries === "function" ? Map : CreateMapPolyfill();
const _Set: typeof Set = typeof Set === "function" && typeof Set.prototype.entries === "function" ? Set : CreateSetPolyfill();
const _WeakMap: typeof WeakMap = typeof WeakMap === "function" ? WeakMap : CreateWeakMapPolyfill();
const registrySymbol = supportsSymbol ? Symbol.for("@reflect-metadata:registry") : undefined;
const metadataRegistry = GetOrCreateMetadataRegistry();
const metadataProvider = CreateMetadataProvider(metadataRegistry);

function decorate(decorators: ClassDecorator[], target: Function): Function;
function decorate(decorators: (PropertyDecorator | MethodDecorator)[], target: any, propertyKey: string | symbol, attributes?: PropertyDescriptor | null): PropertyDescriptor | undefined;
Expand Down Expand Up @@ -1489,14 +1489,19 @@ namespace Reflect {
// - Allows `import "reflect-metadata"` and `import "reflect-metadata/no-conflict"` to interoperate.
// - Uses isolated metadata if `Reflect` is frozen before the registry can be installed.

const registrySymbol = supportsSymbol ? Symbol.for("@reflect-metadata:registry") : undefined;
const metadataRegistry = GetOrCreateMetadataRegistry();
const metadataProvider = CreateMetadataProvider(metadataRegistry);

/**
* Creates a registry used to allow multiple `reflect-metadata` providers.
*/
function CreateMetadataRegistry(): MetadataRegistry {
let fallback: MetadataProvider | undefined;
if (!IsUndefined(registrySymbol) &&
typeof root.Reflect !== "undefined" &&
!(registrySymbol in root.Reflect) &&
typeof root.Reflect.defineMetadata === "function") {
// interoperate with older version of `reflect-metadata` that did not support a registry.
fallback = CreateFallbackProvider(root.Reflect);
}

let first: MetadataProvider | undefined;
let second: MetadataProvider | undefined;
let rest: Set<MetadataProvider> | undefined;
Expand All @@ -1513,6 +1518,7 @@ namespace Reflect {
throw new Error("Cannot add provider to a frozen registry.");
}
switch (true) {
case fallback === provider: break;
case IsUndefined(first): first = provider; break;
case first === provider: break;
case IsUndefined(second): second = provider; break;
Expand All @@ -1525,23 +1531,30 @@ namespace Reflect {
}

function getProviderNoCache(O: object, P: string | symbol | undefined) {
if (IsUndefined(first)) return undefined;
if (first.isProviderFor(O, P)) return first;
if (IsUndefined(second)) return undefined;
if (second.isProviderFor(O, P)) return first;
if (IsUndefined(rest)) return undefined;
const iterator = GetIterator(rest);
while (true) {
const next = IteratorStep(iterator);
if (!next) {
return undefined;
}
const provider = IteratorValue(next);
if (provider.isProviderFor(O, P)) {
IteratorClose(iterator);
return provider;
if (!IsUndefined(first)) {
if (first.isProviderFor(O, P)) return first;
if (!IsUndefined(second)) {
if (second.isProviderFor(O, P)) return first;
if (!IsUndefined(rest)) {
const iterator = GetIterator(rest);
while (true) {
const next = IteratorStep(iterator);
if (!next) {
return undefined;
}
const provider = IteratorValue(next);
if (provider.isProviderFor(O, P)) {
IteratorClose(iterator);
return provider;
}
}
}
}
}
if (!IsUndefined(fallback) && fallback.isProviderFor(O, P)) {
return fallback;
}
return undefined;
}

function getProvider(O: object, P: string | symbol | undefined) {
Expand All @@ -1566,6 +1579,7 @@ namespace Reflect {
}

function hasProvider(provider: MetadataProvider) {
if (IsUndefined(provider)) throw new TypeError();
return first === provider || second === provider || !IsUndefined(rest) && rest.has(provider);
}

Expand Down Expand Up @@ -1730,6 +1744,33 @@ namespace Reflect {
}
}

function CreateFallbackProvider(reflect: typeof Reflect): MetadataProvider {
const metadataOwner = new _WeakMap<object, Set<string | symbol | undefined>>();
const provider: MetadataProvider = {
isProviderFor(O, P) {
let metadataPropertySet = metadataOwner.get(O);
if (!IsUndefined(metadataPropertySet)) {
return metadataPropertySet.has(P);
}
if (reflect.getOwnMetadataKeys(O, P!).length) {
if (IsUndefined(metadataPropertySet)) {
metadataPropertySet = new _Set();
metadataOwner.set(O, metadataPropertySet);
}
metadataPropertySet.add(P);
return true;
}
return false;
},
OrdinaryDefineOwnMetadata: reflect.defineMetadata,
OrdinaryHasOwnMetadata: reflect.hasOwnMetadata,
OrdinaryGetOwnMetadata: reflect.getOwnMetadata,
OrdinaryOwnMetadataKeys: reflect.getOwnMetadataKeys,
OrdinaryDeleteMetadata: reflect.deleteMetadata,
};
return provider;
}

function GetMetadataProvider(O: object, P: string | symbol | undefined, Create: true): MetadataProvider;
function GetMetadataProvider(O: object, P: string | symbol | undefined, Create: false): MetadataProvider | undefined;
/**
Expand Down
105 changes: 74 additions & 31 deletions ReflectLite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -554,20 +554,19 @@ namespace Reflect {
sloppyModeThis();

let exporter = makeExporter(Reflect);
if (typeof root.Reflect === "undefined") {
root.Reflect = Reflect;
}
else {
if (typeof root.Reflect !== "undefined") {
exporter = makeExporter(root.Reflect, exporter);
}

factory(exporter, root);

if (typeof root.Reflect === "undefined") {
root.Reflect = Reflect;
}

function makeExporter(target: typeof Reflect, previous?: <K extends keyof typeof Reflect>(key: K, value: typeof Reflect[K]) => void) {
return <K extends keyof typeof Reflect>(key: K, value: typeof Reflect[K]) => {
if (typeof target[key] !== "function") {
Object.defineProperty(target, key, { configurable: true, writable: true, value });
}
Object.defineProperty(target, key, { configurable: true, writable: true, value });
if (previous) previous(key, value);
};
}
Expand Down Expand Up @@ -595,6 +594,9 @@ namespace Reflect {
const _Map: typeof Map = typeof Map === "function" && typeof Map.prototype.entries === "function" ? Map : fail("A valid Map constructor could not be found.");
const _Set: typeof Set = typeof Set === "function" && typeof Set.prototype.entries === "function" ? Set : fail("A valid Set constructor could not be found.");
const _WeakMap: typeof WeakMap = typeof WeakMap === "function" ? WeakMap : fail("A valid WeakMap constructor could not be found.");
const registrySymbol = supportsSymbol ? Symbol.for("@reflect-metadata:registry") : undefined;
const metadataRegistry = GetOrCreateMetadataRegistry();
const metadataProvider = CreateMetadataProvider(metadataRegistry);

function decorate(decorators: ClassDecorator[], target: Function): Function;
function decorate(decorators: (PropertyDecorator | MethodDecorator)[], target: any, propertyKey: string | symbol, attributes?: PropertyDescriptor | null): PropertyDescriptor | undefined;
Expand Down Expand Up @@ -1455,14 +1457,19 @@ namespace Reflect {
// - Allows `import "reflect-metadata"` and `import "reflect-metadata/no-conflict"` to interoperate.
// - Uses isolated metadata if `Reflect` is frozen before the registry can be installed.

const registrySymbol = supportsSymbol ? Symbol.for("@reflect-metadata:registry") : undefined;
const metadataRegistry = GetOrCreateMetadataRegistry();
const metadataProvider = CreateMetadataProvider(metadataRegistry);

/**
* Creates a registry used to allow multiple `reflect-metadata` providers.
*/
function CreateMetadataRegistry(): MetadataRegistry {
let fallback: MetadataProvider | undefined;
if (!IsUndefined(registrySymbol) &&
typeof root.Reflect !== "undefined" &&
!(registrySymbol in root.Reflect) &&
typeof root.Reflect.defineMetadata === "function") {
// interoperate with older version of `reflect-metadata` that did not support a registry.
fallback = CreateFallbackProvider(root.Reflect);
}

let first: MetadataProvider | undefined;
let second: MetadataProvider | undefined;
let rest: Set<MetadataProvider> | undefined;
Expand All @@ -1479,6 +1486,7 @@ namespace Reflect {
throw new Error("Cannot add provider to a frozen registry.");
}
switch (true) {
case fallback === provider: break;
case IsUndefined(first): first = provider; break;
case first === provider: break;
case IsUndefined(second): second = provider; break;
Expand All @@ -1489,27 +1497,34 @@ namespace Reflect {
break;
}
}

function getProviderNoCache(O: object, P: string | symbol | undefined) {
if (IsUndefined(first)) return undefined;
if (first.isProviderFor(O, P)) return first;
if (IsUndefined(second)) return undefined;
if (second.isProviderFor(O, P)) return first;
if (IsUndefined(rest)) return undefined;
const iterator = GetIterator(rest);
while (true) {
const next = IteratorStep(iterator);
if (!next) {
return undefined;
}
const provider = IteratorValue(next);
if (provider.isProviderFor(O, P)) {
IteratorClose(iterator);
return provider;
if (!IsUndefined(first)) {
if (first.isProviderFor(O, P)) return first;
if (!IsUndefined(second)) {
if (second.isProviderFor(O, P)) return first;
if (!IsUndefined(rest)) {
const iterator = GetIterator(rest);
while (true) {
const next = IteratorStep(iterator);
if (!next) {
return undefined;
}
const provider = IteratorValue(next);
if (provider.isProviderFor(O, P)) {
IteratorClose(iterator);
return provider;
}
}
}
}
}
if (!IsUndefined(fallback) && fallback.isProviderFor(O, P)) {
return fallback;
}
return undefined;
}

function getProvider(O: object, P: string | symbol | undefined) {
let providerMap = targetProviderMap.get(O);
let provider: MetadataProvider | undefined;
Expand All @@ -1519,7 +1534,7 @@ namespace Reflect {
if (!IsUndefined(provider)) {
return provider;
}

provider = getProviderNoCache(O, P);
if (!IsUndefined(provider)) {
if (IsUndefined(providerMap)) {
Expand All @@ -1530,11 +1545,12 @@ namespace Reflect {
}
return provider;
}

function hasProvider(provider: MetadataProvider) {
if (IsUndefined(provider)) throw new TypeError();
return first === provider || second === provider || !IsUndefined(rest) && rest.has(provider);
}

function setProvider(O: object, P: string | symbol | undefined, provider: MetadataProvider) {
if (!hasProvider(provider)) {
throw new Error("Metadata provider not registered.");
Expand Down Expand Up @@ -1696,6 +1712,33 @@ namespace Reflect {
}
}

function CreateFallbackProvider(reflect: typeof Reflect): MetadataProvider {
const metadataOwner = new _WeakMap<object, Set<string | symbol | undefined>>();
const provider: MetadataProvider = {
isProviderFor(O, P) {
let metadataPropertySet = metadataOwner.get(O);
if (!IsUndefined(metadataPropertySet)) {
return metadataPropertySet.has(P);
}
if (reflect.getOwnMetadataKeys(O, P!).length) {
if (IsUndefined(metadataPropertySet)) {
metadataPropertySet = new _Set();
metadataOwner.set(O, metadataPropertySet);
}
metadataPropertySet.add(P);
return true;
}
return false;
},
OrdinaryDefineOwnMetadata: reflect.defineMetadata,
OrdinaryHasOwnMetadata: reflect.hasOwnMetadata,
OrdinaryGetOwnMetadata: reflect.getOwnMetadata,
OrdinaryOwnMetadataKeys: reflect.getOwnMetadataKeys,
OrdinaryDeleteMetadata: reflect.deleteMetadata,
};
return provider;
}

function GetMetadataProvider(O: object, P: string | symbol | undefined, Create: true): MetadataProvider;
function GetMetadataProvider(O: object, P: string | symbol | undefined, Create: false): MetadataProvider | undefined;
/**
Expand Down
Loading

0 comments on commit 5a9d20a

Please sign in to comment.