Lazy decoration (part 8, consistent call order) (#4153)

* Lazy decoration (part 8, consistent call order)

* Dedupe

* Flatten, no extra calls

* Dedupe more

* Adjust lazyVariants

* Additional flatten

* Adjust lazyVariants

* Reorder name/index

* Deupe strings

* Flatten createkeys

* Adjust
This commit is contained in:
Jaco
2021-11-03 10:47:04 +02:00
committed by GitHub
parent 5b0652c272
commit f5f67e795e
14 changed files with 192 additions and 223 deletions
+18 -1
View File
@@ -1,8 +1,14 @@
// Copyright 2017-2021 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { PortableRegistry, SiLookupTypeId, SiVariant } from '../interfaces';
import { isUndefined } from '@polkadot/util';
interface TypeHolder {
type: SiLookupTypeId
}
type WithToString = { toString: () => string };
export function lazyMethod <T, K> (result: Record<string, T>, item: K, creator: (d: K) => T, getName?: (d: K) => string): void {
@@ -44,10 +50,21 @@ export function lazyMethod <T, K> (result: Record<string, T>, item: K, creator:
});
}
export function lazyMethods <T, K> (items: K[], creator: (v: K) => T, getName?: (m: K) => string, result: Record<string, T> = {}): Record<string, T> {
export function lazyMethods <T, K> (result: Record<string, T>, items: K[], creator: (v: K) => T, getName?: (m: K) => string): Record<string, T> {
for (let i = 0; i < items.length; i++) {
lazyMethod(result, items[i], creator, getName);
}
return result;
}
export function lazyVariants <T> (lookup: PortableRegistry, { type }: TypeHolder, getName: (v: SiVariant) => string, creator: (v: SiVariant) => T): Record<string, T> {
const result: Record<string, T> = {};
const variants = lookup.getSiType(type).def.asVariant.variants;
for (let i = 0; i < variants.length; i++) {
lazyMethod(result, variants[i], creator, getName);
}
return result;
}
+38 -68
View File
@@ -2,7 +2,7 @@
// SPDX-License-Identifier: Apache-2.0
import type { ExtDef } from '../extrinsic/signedExtensions/types';
import type { ChainProperties, CodecHash, DispatchErrorModule, Hash, MetadataLatest, PalletMetadataLatest, PortableRegistry, SiField, SiLookupTypeId, SiVariant } from '../interfaces/types';
import type { ChainProperties, CodecHash, DispatchErrorModule, Hash, MetadataLatest, PortableRegistry, SiField, SiLookupTypeId, SiVariant } from '../interfaces/types';
import type { CallFunction, Codec, CodecHasher, Constructor, DetectCodec, DetectConstructor, RegisteredTypes, Registry, RegistryError, RegistryTypes } from '../types';
import type { CreateOptions } from './types';
@@ -21,7 +21,7 @@ import { createCallFunction } from '../metadata/decorate/extrinsics';
import { Metadata } from '../metadata/Metadata';
import { createClass } from './createClass';
import { createTypeUnsafe } from './createType';
import { lazyMethod, lazyMethods } from './lazy';
import { lazyMethod, lazyVariants } from './lazy';
const l = logger('registry');
@@ -47,106 +47,76 @@ function clearRecord (record: Record<string, unknown>): void {
}
}
function getVariants (lookup: PortableRegistry, { type }: { type: SiLookupTypeId }): SiVariant[] {
return lookup.getSiType(type).def.asVariant.variants;
}
function getVariantStringIdx ({ index }: SiVariant): string {
return index.toString();
}
// create error mapping from metadata
function injectErrors (_: Registry, { lookup, pallets }: MetadataLatest, version: number, result: Record<string, Record<string, RegistryError>>): void {
const lazySection = ({ errors, name }: PalletMetadataLatest, index: number): void => {
const section = stringCamelCase(name);
clearRecord(result);
lazyMethod(result, index, () =>
lazyMethods(
getVariants(lookup, errors.unwrap()),
({ docs, fields, index, name }: SiVariant): RegistryError => ({
for (let i = 0; i < pallets.length; i++) {
const { errors, index, name } = pallets[i];
if (errors.isSome) {
const sectionName = stringCamelCase(name);
lazyMethod(result, version >= 12 ? index.toNumber() : i, () =>
lazyVariants(lookup, errors.unwrap(), getVariantStringIdx, ({ docs, fields, index, name }: SiVariant): RegistryError => ({
args: getFieldArgs(lookup, fields),
docs: docs.map(valueToString),
fields,
index: index.toNumber(),
method: name.toString(),
name: name.toString(),
section
}),
getVariantStringIdx
)
);
};
clearRecord(result);
for (let i = 0; i < pallets.length; i++) {
const pallet = pallets[i];
if (pallet.errors.isSome) {
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : i);
section: sectionName
}))
);
}
}
}
// create event classes from metadata
function injectEvents (registry: Registry, { lookup, pallets }: MetadataLatest, version: number, result: Record<string, Record<string, Constructor<GenericEventData>>>): void {
const lazySection = ({ events, name }: PalletMetadataLatest, index: number): void => {
const section = stringCamelCase(name);
lazyMethod(result, index, () =>
lazyMethods(
getVariants(lookup, events.unwrap()),
(variant: SiVariant): Constructor<GenericEventData> => {
const meta = registry.createType('EventMetadataLatest', {
...variant,
args: getFieldArgs(lookup, variant.fields)
});
return class extends GenericEventData {
constructor (registry: Registry, value: Uint8Array) {
super(registry, value, meta, section, variant.name.toString());
}
};
},
getVariantStringIdx
)
);
};
const filtered = pallets.filter(filterEventsSome);
clearRecord(result);
const filtered = pallets.filter(filterEventsSome);
for (let i = 0; i < filtered.length; i++) {
const pallet = filtered[i];
const { events, index, name } = filtered[i];
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : i);
lazyMethod(result, version >= 12 ? index.toNumber() : i, () =>
lazyVariants(lookup, events.unwrap(), getVariantStringIdx, (variant: SiVariant): Constructor<GenericEventData> => {
const meta = registry.createType('EventMetadataLatest', {
...variant,
args: getFieldArgs(lookup, variant.fields)
});
return class extends GenericEventData {
constructor (registry: Registry, value: Uint8Array) {
super(registry, value, meta, stringCamelCase(name), variant.name.toString());
}
};
})
);
}
}
// create extrinsic mapping from metadata
function injectExtrinsics (registry: Registry, { lookup, pallets }: MetadataLatest, version: number, result: Record<string, Record<string, CallFunction>>): void {
const lazySection = ({ calls, name }: PalletMetadataLatest, index: number): void => {
const section = stringCamelCase(name);
lazyMethod(result, index, () =>
lazyMethods(
getVariants(lookup, calls.unwrap()),
(variant: SiVariant) =>
createCallFunction(registry, lookup, variant, index, section),
getVariantStringIdx
)
);
};
const filtered = pallets.filter(filterCallsSome);
clearRecord(result);
const filtered = pallets.filter(filterCallsSome);
for (let i = 0; i < filtered.length; i++) {
const pallet = filtered[i];
const { calls, index, name } = filtered[i];
const sectionIndex = version >= 12 ? index.toNumber() : i;
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : i);
lazyMethod(result, sectionIndex, () =>
lazyVariants(lookup, calls.unwrap(), getVariantStringIdx, (variant: SiVariant) =>
createCallFunction(registry, lookup, variant, stringCamelCase(name), sectionIndex)
)
);
}
}
@@ -1,7 +1,7 @@
// Copyright 2017-2021 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { MetadataLatest, PalletConstantMetadataLatest, PalletMetadataLatest } from '../../../interfaces';
import type { MetadataLatest, PalletConstantMetadataLatest } from '../../../interfaces';
import type { Registry } from '../../../types';
import type { ConstantCodec, Constants } from '../types';
@@ -10,35 +10,29 @@ import { hexToU8a, stringCamelCase } from '@polkadot/util';
import { lazyMethod, lazyMethods } from '../../../create/lazy';
import { objectNameToCamel } from '../util';
function createConstantCodec (registry: Registry, constant: PalletConstantMetadataLatest): ConstantCodec {
const codec = registry.createTypeUnsafe(registry.createLookupType(constant.type), [hexToU8a(constant.value.toHex())]) as ConstantCodec;
(codec as unknown as Record<string, unknown>).meta = constant;
return codec;
}
/** @internal */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function decorateConstants (registry: Registry, { pallets }: MetadataLatest, _version: number): Constants {
const result: Constants = {};
const lazySection = ({ constants, name }: PalletMetadataLatest): void => {
lazyMethod(result, stringCamelCase(name), () =>
lazyMethods(
constants,
(constant: PalletConstantMetadataLatest) =>
createConstantCodec(registry, constant),
objectNameToCamel
)
);
};
for (let i = 0; i < pallets.length; i++) {
const { constants, name } = pallets[i];
for (let p = 0; p < pallets.length; p++) {
const pallet = pallets[p];
if (!constants.isEmpty) {
lazyMethod(result, stringCamelCase(name), () =>
lazyMethods(
{},
constants,
(constant: PalletConstantMetadataLatest): ConstantCodec => {
const codec = registry.createTypeUnsafe(registry.createLookupType(constant.type), [hexToU8a(constant.value.toHex())]) as ConstantCodec;
if (!pallet.constants.isEmpty) {
lazySection(pallet);
(codec as unknown as Record<string, unknown>).meta = constant;
return codec;
},
objectNameToCamel
)
);
}
}
@@ -1,14 +1,14 @@
// Copyright 2017-2021 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { DispatchErrorModule, MetadataLatest, PalletMetadataLatest, PortableRegistry, SiField, SiVariant } from '../../../interfaces';
import type { DispatchErrorModule, MetadataLatest, PortableRegistry, SiField, SiVariant } from '../../../interfaces';
import type { Text, u8 } from '../../../primitive';
import type { Registry } from '../../../types';
import type { Errors, IsError } from '../types';
import { stringCamelCase } from '@polkadot/util';
import { lazyMethod, lazyMethods } from '../../../create/lazy';
import { lazyMethod, lazyVariants } from '../../../create/lazy';
import { objectNameToString } from '../util';
interface ItemMeta {
@@ -28,35 +28,24 @@ export function variantToMeta (lookup: PortableRegistry, variant: SiVariant): It
};
}
function createIsError (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number): IsError {
return {
is: ({ error, index }: DispatchErrorModule) =>
index.eq(sectionIndex) &&
error.eq(variant.index),
meta: registry.createType('ErrorMetadataLatest', variantToMeta(lookup, variant))
};
}
/** @internal */
export function decorateErrors (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Errors {
const result: Errors = {};
const lazySection = ({ errors, name }: PalletMetadataLatest, sectionIndex: number): void => {
lazyMethod(result, stringCamelCase(name), () =>
lazyMethods(
lookup.getSiType(errors.unwrap().type).def.asVariant.variants,
(v: SiVariant) =>
createIsError(registry, lookup, v, sectionIndex),
objectNameToString
)
);
};
for (let i = 0; i < pallets.length; i++) {
const { errors, index, name } = pallets[i];
for (let p = 0; p < pallets.length; p++) {
const pallet = pallets[p];
if (errors.isSome) {
const sectionIndex = version >= 12 ? index.toNumber() : i;
if (pallet.errors.isSome) {
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p);
lazyMethod(result, stringCamelCase(name), () =>
lazyVariants(lookup, errors.unwrap(), objectNameToString, (variant: SiVariant): IsError => ({
is: ({ error, index }: DispatchErrorModule) =>
index.eq(sectionIndex) &&
error.eq(variant.index),
meta: registry.createType('ErrorMetadataLatest', variantToMeta(lookup, variant))
}))
);
}
}
@@ -1,13 +1,13 @@
// Copyright 2017-2021 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { MetadataLatest, PalletMetadataLatest, PortableRegistry, SiVariant } from '../../../interfaces';
import type { MetadataLatest, PalletMetadataLatest, SiVariant } from '../../../interfaces';
import type { AnyTuple, IEvent, Registry } from '../../../types';
import type { Events, IsEvent } from '../types';
import { stringCamelCase } from '@polkadot/util';
import { lazyMethod, lazyMethods } from '../../../create/lazy';
import { lazyMethod, lazyVariants } from '../../../create/lazy';
import { variantToMeta } from '../errors';
import { objectNameToString } from '../util';
@@ -15,36 +15,23 @@ export function filterEventsSome ({ events }: PalletMetadataLatest): boolean {
return events.isSome;
}
function createIsEvent (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number): IsEvent<AnyTuple> {
return {
is: <T extends AnyTuple> (eventRecord: IEvent<AnyTuple>): eventRecord is IEvent<T> =>
sectionIndex === eventRecord.index[0] &&
variant.index.eq(eventRecord.index[1]),
meta: registry.createType('EventMetadataLatest', variantToMeta(lookup, variant))
};
}
/** @internal */
export function decorateEvents (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Events {
const result: Events = {};
const lazySection = ({ events, name }: PalletMetadataLatest, sectionIndex: number): void => {
lazyMethod(result, stringCamelCase(name), () =>
lazyMethods(
lookup.getSiType(events.unwrap().type).def.asVariant.variants,
(v: SiVariant) =>
createIsEvent(registry, lookup, v, sectionIndex),
objectNameToString
)
);
};
const filtered = pallets.filter(filterEventsSome);
for (let p = 0; p < filtered.length; p++) {
const pallet = filtered[p];
for (let i = 0; i < filtered.length; i++) {
const { events, index, name } = filtered[i];
const sectionIndex = version >= 12 ? index.toNumber() : i;
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p);
lazyMethod(result, stringCamelCase(name), () =>
lazyVariants(lookup, events.unwrap(), objectNameToString, (variant: SiVariant): IsEvent<AnyTuple> => ({
is: <T extends AnyTuple> (eventRecord: IEvent<AnyTuple>): eventRecord is IEvent<T> =>
sectionIndex === eventRecord.index[0] &&
variant.index.eq(eventRecord.index[1]),
meta: registry.createType('EventMetadataLatest', variantToMeta(lookup, variant))
}))
);
}
return result;
@@ -7,7 +7,7 @@ import type { Extrinsics } from '../types';
import { stringCamelCase } from '@polkadot/util';
import { lazyMethod, lazyMethods } from '../../../create/lazy';
import { lazyMethod, lazyVariants } from '../../../create/lazy';
import { getSiName } from '../../util';
import { objectNameToCamel } from '../util';
import { createUnchecked } from './createUnchecked';
@@ -16,7 +16,7 @@ export function filterCallsSome ({ calls }: PalletMetadataLatest): boolean {
return calls.isSome;
}
export function createCallFunction (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionIndex: number, sectionName: string): CallFunction {
export function createCallFunction (registry: Registry, lookup: PortableRegistry, variant: SiVariant, sectionName: string, sectionIndex: number): CallFunction {
const { fields, index } = variant;
const args = new Array<Record<string, unknown>>(fields.length);
@@ -44,26 +44,18 @@ export function createCallFunction (registry: Registry, lookup: PortableRegistry
/** @internal */
export function decorateExtrinsics (registry: Registry, { lookup, pallets }: MetadataLatest, version: number): Extrinsics {
const result: Extrinsics = {};
const lazySection = ({ calls, name }: PalletMetadataLatest, sectionIndex: number): void => {
const sectionName = stringCamelCase(name);
lazyMethod(result, sectionName, () =>
lazyMethods(
lookup.getSiType(calls.unwrap().type).def.asVariant.variants,
(v: SiVariant) =>
createCallFunction(registry, lookup, v, sectionIndex, sectionName),
objectNameToCamel
)
);
};
const filtered = pallets.filter(filterCallsSome);
for (let p = 0; p < filtered.length; p++) {
const pallet = filtered[p];
for (let i = 0; i < filtered.length; i++) {
const { calls, index, name } = filtered[i];
const sectionName = stringCamelCase(name);
const sectionIndex = version >= 12 ? index.toNumber() : i;
lazySection(pallet, version >= 12 ? pallet.index.toNumber() : p);
lazyMethod(result, sectionName, () =>
lazyVariants(lookup, calls.unwrap(), objectNameToCamel, (variant: SiVariant) =>
createCallFunction(registry, lookup, variant, sectionName, sectionIndex)
)
);
}
return result;
@@ -35,14 +35,18 @@ interface IterFn {
/** @internal */
export function createKeyRaw (registry: Registry, itemFn: CreateItemBase, keys: SiLookupTypeId[], hashers: StorageHasher[], args: unknown[]): Uint8Array {
const extra = new Array<Uint8Array>(keys.length);
for (let i = 0; i < keys.length; i++) {
extra[i] = getHasher(hashers[i])(
registry.createType(registry.createLookupType(keys[i]), args[i]).toU8a()
);
}
return u8aConcat(
xxhashAsU8a(itemFn.prefix, 128),
xxhashAsU8a(itemFn.method, 128),
...keys.map((type, index) =>
getHasher(hashers[index])(
registry.createType(registry.createLookupType(type), args[index]).toU8a()
)
)
...extra
);
}
@@ -120,10 +124,9 @@ function extendPrefixedMap (registry: Registry, itemFn: CreateItemFn, storageFn:
const { hashers, key } = type.asMap;
const keysVec = hashers.length === 1
? [key]
: [...registry.lookup.getSiType(key).def.asTuple.map((t) => t)];
const hashersVec = [...hashers];
: registry.lookup.getSiType(key).def.asTuple;
return new Raw(registry, createKeyRaw(registry, itemFn, keysVec.slice(0, args.length), hashersVec.slice(0, args.length), args));
return new Raw(registry, createKeyRaw(registry, itemFn, keysVec.slice(0, args.length), hashers.slice(0, args.length), args));
}
}
@@ -136,6 +139,7 @@ function extendPrefixedMap (registry: Registry, itemFn: CreateItemFn, storageFn:
/** @internal */
export function createFunction (registry: Registry, itemFn: CreateItemFn, options: CreateItemOptions): StorageEntry {
const { meta: { type } } = itemFn;
let cacheKey: Uint8Array | null = null;
// Can only have zero or one argument:
// - storage.system.account(address)
@@ -143,16 +147,20 @@ export function createFunction (registry: Registry, itemFn: CreateItemFn, option
// For higher-map queries the params are passed in as an tuple, [key1, key2]
const storageFn = expandWithMeta(itemFn, (...args: unknown[]): Uint8Array => {
if (type.isPlain) {
return options.skipHashing
? compactAddLength(u8aToU8a(options.key))
: createKey(registry, itemFn, [], [], []);
if (!cacheKey) {
cacheKey = options.skipHashing
? compactAddLength(u8aToU8a(options.key))
: createKey(registry, itemFn, [], [], []);
}
return cacheKey;
}
const { hashers, key } = type.asMap;
return hashers.length === 1
? createKey(registry, itemFn, [key], hashers, args)
: createKey(registry, itemFn, registry.lookup.getSiType(key).def.asTuple.map((t) => t), hashers, args);
: createKey(registry, itemFn, registry.lookup.getSiType(key).def.asTuple, hashers, args);
});
if (type.isMap) {
@@ -1,7 +1,7 @@
// Copyright 2017-2021 @polkadot/types authors & contributors
// SPDX-License-Identifier: Apache-2.0
import type { MetadataLatest, PalletMetadataLatest, StorageEntryMetadataLatest } from '../../../interfaces';
import type { MetadataLatest, StorageEntryMetadataLatest } from '../../../interfaces';
import type { Registry } from '../../../types';
import type { Storage } from '../types';
@@ -13,38 +13,38 @@ import { createFunction, createKeyRaw } from './createFunction';
import { getStorage } from './getStorage';
import { createRuntimeFunction } from './util';
const VERSION_NAME = 'palletVersion';
const VERSION_KEY = ':__STORAGE_VERSION__:';
const VERSION_DOCS = { docs: 'Returns the current pallet version from storage', type: 'u16' };
/** @internal */
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export function decorateStorage (registry: Registry, { pallets }: MetadataLatest, _metaVersion: number): Storage {
const result: Storage = getStorage(registry);
const lazySection = ({ name, storage }: PalletMetadataLatest): void => {
const section = stringCamelCase(name);
const { items, prefix: _prefix } = storage.unwrap();
const prefix = _prefix.toString();
for (let i = 0; i < pallets.length; i++) {
const { name, storage } = pallets[i];
lazyMethod(result, section, () =>
lazyMethods(
items,
(meta: StorageEntryMetadataLatest) =>
createFunction(registry, { meta, method: meta.name.toString(), prefix, section }, {}),
objectNameFirstLower,
{
palletVersion: createRuntimeFunction(
{ method: 'palletVersion', prefix, section },
createKeyRaw(registry, { method: ':__STORAGE_VERSION__:', prefix: name.toString() }, [], [], []),
{ docs: 'Returns the current pallet version from storage', type: 'u16' }
)(registry)
}
)
);
};
if (storage.isSome) {
const section = stringCamelCase(name);
const { items, prefix: _prefix } = storage.unwrap();
const prefix = _prefix.toString();
for (let p = 0; p < pallets.length; p++) {
const pallet = pallets[p];
if (pallet.storage.isSome) {
lazySection(pallet);
lazyMethod(result, section, () =>
lazyMethods(
{
palletVersion: createRuntimeFunction(
{ method: VERSION_NAME, prefix, section },
createKeyRaw(registry, { method: VERSION_KEY, prefix: name.toString() }, [], [], []),
VERSION_DOCS
)(registry)
},
items,
(meta: StorageEntryMetadataLatest) =>
createFunction(registry, { meta, method: meta.name.toString(), prefix, section }, {}),
objectNameFirstLower
)
);
}
}
@@ -9,29 +9,32 @@ import { createRuntimeFunction } from './util';
type Creator = (registry: Registry) => StorageEntry;
const prefix = 'Substrate';
const section = 'substrate';
function createSubstrateFn (method: string, key: string, meta: ManualMetadata): Creator {
return createRuntimeFunction({ method, prefix: 'Substrate', section: 'substrate' }, key, meta);
return createRuntimeFunction({ method, prefix, section }, key, meta);
}
export const substrate: Record<string, Creator> = {
changesTrieConfig: createSubstrateFn('changesTrieConfig', ':changes_trie', {
docs: ' Changes trie configuration is stored under this key.',
docs: 'Changes trie configuration is stored under this key.',
type: 'u32'
}),
childStorageKeyPrefix: createSubstrateFn('childStorageKeyPrefix', ':child_storage:', {
docs: ' Prefix of child storage keys.',
docs: 'Prefix of child storage keys.',
type: 'u32'
}),
code: createSubstrateFn('code', ':code', {
docs: ' Wasm code of the runtime.',
docs: 'Wasm code of the runtime.',
type: 'Bytes'
}),
extrinsicIndex: createSubstrateFn('extrinsicIndex', ':extrinsic_index', {
docs: ' Current extrinsic index (u32) is stored under this key.',
docs: 'Current extrinsic index (u32) is stored under this key.',
type: 'u32'
}),
heapPages: createSubstrateFn('heapPages', ':heappages', {
docs: ' Number of wasm linear memory pages required for execution of the runtime.',
docs: 'Number of wasm linear memory pages required for execution of the runtime.',
type: 'u64'
})
};
@@ -21,7 +21,7 @@ interface ManualDefinition {
function findSiPrimitive (registry: Registry, _prim: string): PortableType | undefined {
const prim = _prim.toLowerCase();
const portable = registry.lookup.types.find((t) =>
return registry.lookup.types.find((t) =>
(
t.type.def.isPrimitive &&
t.type.def.asPrimitive.toString().toLowerCase() === prim
@@ -30,8 +30,6 @@ function findSiPrimitive (registry: Registry, _prim: string): PortableType | und
t.type.def.asHistoricMetaCompat.toString().toLowerCase() === prim
)
);
return portable;
}
function findSiType (registry: Registry, orig: string): PortableType | undefined {