Cache class definitions for codec primitives (#4855)
* Cache class definitions for codec primitives * Adjustments for linting * clearCache * cleaner test description * inline assign * Adjust failing * Pass definition via Options
This commit is contained in:
@@ -9,6 +9,15 @@ import { compactFromU8a, compactToU8a, isU8a } from '@polkadot/util';
|
||||
|
||||
import { typeToConstructor } from '../utils';
|
||||
|
||||
interface Options<T> {
|
||||
definition?: CodecClass<T>;
|
||||
setDefinition?: (d: CodecClass<T>) => CodecClass<T>;
|
||||
}
|
||||
|
||||
function noopSetDefinition <T> (d: CodecClass<T>): CodecClass<T> {
|
||||
return d;
|
||||
}
|
||||
|
||||
/**
|
||||
* @name Compact
|
||||
* @description
|
||||
@@ -28,9 +37,9 @@ export class Compact<T extends INumber> implements ICompact<T> {
|
||||
|
||||
readonly #raw: T;
|
||||
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, value: Compact<T> | AnyNumber = 0) {
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, value: Compact<T> | AnyNumber = 0, { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
|
||||
this.registry = registry;
|
||||
this.#Type = typeToConstructor(registry, Type);
|
||||
this.#Type = definition || setDefinition(typeToConstructor(registry, Type));
|
||||
|
||||
const [raw, decodedLength] = Compact.decodeCompact<T>(registry, this.#Type, value);
|
||||
|
||||
@@ -38,10 +47,16 @@ export class Compact<T extends INumber> implements ICompact<T> {
|
||||
this.#raw = raw;
|
||||
}
|
||||
|
||||
public static with<T extends INumber> (Type: CodecClass<T> | string): CodecClass<Compact<T>> {
|
||||
return class extends Compact<T> {
|
||||
constructor (registry: Registry, value?: Compact<T> | AnyNumber) {
|
||||
super(registry, Type, value);
|
||||
public static with<O extends INumber> (Type: CodecClass<O> | string): CodecClass<Compact<O>> {
|
||||
let definition: CodecClass<O> | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = <T> (d: CodecClass<T>) =>
|
||||
(definition = d as unknown as CodecClass<O>) as unknown as CodecClass<T>;
|
||||
|
||||
return class extends Compact<O> {
|
||||
constructor (registry: Registry, value?: Compact<O> | AnyNumber) {
|
||||
super(registry, Type, value, { definition, setDefinition });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -14,6 +14,12 @@ export interface EnumCodecClass<T = Codec> {
|
||||
new(registry: Registry, value?: any, index?: number): T;
|
||||
}
|
||||
|
||||
interface Definition {
|
||||
def: TypesDef;
|
||||
isBasic: boolean;
|
||||
isIndexed: boolean;
|
||||
}
|
||||
|
||||
interface EntryDef {
|
||||
Type: CodecClass;
|
||||
index: number;
|
||||
@@ -26,6 +32,15 @@ interface Decoded {
|
||||
value: Codec;
|
||||
}
|
||||
|
||||
interface Options {
|
||||
definition?: Definition;
|
||||
setDefinition?: (d: Definition) => Definition;
|
||||
}
|
||||
|
||||
function noopSetDefinition (d: Definition): Definition {
|
||||
return d;
|
||||
}
|
||||
|
||||
function isRustEnum (def: Record<string, string | CodecClass> | Record<string, number>): def is Record<string, string | CodecClass> {
|
||||
const defValues = Object.values(def);
|
||||
|
||||
@@ -38,7 +53,7 @@ function isRustEnum (def: Record<string, string | CodecClass> | Record<string, n
|
||||
return true;
|
||||
}
|
||||
|
||||
function extractDef (registry: Registry, _def: Record<string, string | CodecClass> | Record<string, number> | string[]): { def: TypesDef; isBasic: boolean; isIndexed: boolean } {
|
||||
function extractDef (registry: Registry, _def: Record<string, string | CodecClass> | Record<string, number> | string[]): Definition {
|
||||
const def: TypesDef = {};
|
||||
let isBasic: boolean;
|
||||
let isIndexed: boolean;
|
||||
@@ -163,8 +178,8 @@ export class Enum implements IEnum {
|
||||
|
||||
readonly #raw: Codec;
|
||||
|
||||
constructor (registry: Registry, Types: Record<string, string | CodecClass> | Record<string, number> | string[], value?: unknown, index?: number) {
|
||||
const { def, isBasic, isIndexed } = extractDef(registry, Types);
|
||||
constructor (registry: Registry, Types: Record<string, string | CodecClass> | Record<string, number> | string[], value?: unknown, index?: number, { definition, setDefinition = noopSetDefinition }: Options = {}) {
|
||||
const { def, isBasic, isIndexed } = definition || setDefinition(extractDef(registry, Types));
|
||||
|
||||
// shortcut isU8a as used in SCALE decoding
|
||||
const decoded = isU8a(value) && value.length && !isNumber(index)
|
||||
@@ -198,9 +213,15 @@ export class Enum implements IEnum {
|
||||
isKeys[i] = `is${name}`;
|
||||
}
|
||||
|
||||
let definition: Definition | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = (d: Definition) =>
|
||||
definition = d;
|
||||
|
||||
return class extends Enum {
|
||||
constructor (registry: Registry, value?: unknown, index?: number) {
|
||||
super(registry, Types, value, index);
|
||||
super(registry, Types, value, index, { definition, setDefinition });
|
||||
|
||||
objectProperties(this, isKeys, (_, i) => this.type === keys[i]);
|
||||
objectProperties(this, asKeys, (k, i): Codec => {
|
||||
|
||||
@@ -9,6 +9,15 @@ import { assert, isCodec, isNull, isU8a, isUndefined, u8aToHex } from '@polkadot
|
||||
import { typeToConstructor } from '../utils';
|
||||
import { Null } from './Null';
|
||||
|
||||
interface Options<T> {
|
||||
definition?: CodecClass<T>;
|
||||
setDefinition?: (d: CodecClass<T>) => CodecClass<T>;
|
||||
}
|
||||
|
||||
function noopSetDefinition <T extends Codec> (d: CodecClass<T>): CodecClass<T> {
|
||||
return d;
|
||||
}
|
||||
|
||||
class None extends Null {
|
||||
/**
|
||||
* @description Returns the base runtime type name for this instance
|
||||
@@ -60,8 +69,8 @@ export class Option<T extends Codec> implements IOption<T> {
|
||||
|
||||
readonly #raw: T;
|
||||
|
||||
constructor (registry: Registry, typeName: CodecClass<T> | string, value?: unknown) {
|
||||
const Type = typeToConstructor(registry, typeName);
|
||||
constructor (registry: Registry, typeName: CodecClass<T> | string, value?: unknown, { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
|
||||
const Type = definition || setDefinition(typeToConstructor(registry, typeName));
|
||||
const decoded = isU8a(value) && value.length && !isCodec(value)
|
||||
? value[0] === 0
|
||||
? new None(registry)
|
||||
@@ -78,9 +87,17 @@ export class Option<T extends Codec> implements IOption<T> {
|
||||
}
|
||||
|
||||
public static with<O extends Codec> (Type: CodecClass<O> | string): CodecClass<Option<O>> {
|
||||
let definition: CodecClass<O> | undefined;
|
||||
|
||||
const setDefinition = <T> (d: CodecClass<T>): CodecClass<T> => {
|
||||
definition = d as unknown as CodecClass<O>;
|
||||
|
||||
return d;
|
||||
};
|
||||
|
||||
return class extends Option<O> {
|
||||
constructor (registry: Registry, value?: unknown) {
|
||||
super(registry, Type, value);
|
||||
super(registry, Type, value, { definition, setDefinition });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ import { isFunction, isHex, isString, isU8a, stringify, u8aConcat, u8aToU8a } fr
|
||||
import { AbstractArray } from '../abstract/AbstractArray';
|
||||
import { decodeU8a, mapToTypeMap, typeToConstructor } from '../utils';
|
||||
|
||||
type TupleCodecClasss = CodecClass[] | {
|
||||
type TupleCodecClass = CodecClass[] | {
|
||||
[index: string]: CodecClass;
|
||||
};
|
||||
|
||||
@@ -18,8 +18,17 @@ type TupleTypes = TupleType[] | {
|
||||
[index: string]: CodecClass | string;
|
||||
};
|
||||
|
||||
interface Options {
|
||||
definition?: TupleCodecClass;
|
||||
setDefinition?: (d: TupleCodecClass) => TupleCodecClass;
|
||||
}
|
||||
|
||||
function noopSetDefinition (d: TupleCodecClass): TupleCodecClass {
|
||||
return d;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeTuple (registry: Registry, Classes: TupleCodecClasss, value?: Exclude<AnyTupleValue, Uint8Array>): [Codec[], number] {
|
||||
function decodeTuple (registry: Registry, Classes: TupleCodecClass, value?: Exclude<AnyTupleValue, Uint8Array>): [Codec[], number] {
|
||||
if (isU8a(value) || isHex(value)) {
|
||||
return decodeU8a(registry, u8aToU8a(value), Classes);
|
||||
}
|
||||
@@ -53,14 +62,15 @@ function decodeTuple (registry: Registry, Classes: TupleCodecClasss, value?: Exc
|
||||
* own type. It extends the base JS `Array` object.
|
||||
*/
|
||||
export class Tuple extends AbstractArray<Codec> implements ITuple<Codec[]> {
|
||||
#Types: TupleCodecClasss;
|
||||
#Types: TupleCodecClass;
|
||||
|
||||
constructor (registry: Registry, Types: TupleTypes | TupleType, value?: AnyTupleValue) {
|
||||
const Classes = Array.isArray(Types)
|
||||
constructor (registry: Registry, Types: TupleTypes | TupleType, value?: AnyTupleValue, { definition, setDefinition = noopSetDefinition }: Options = {}) {
|
||||
const Classes = definition || setDefinition(Array.isArray(Types)
|
||||
? Types.map((t) => typeToConstructor(registry, t))
|
||||
: isFunction(Types) || isString(Types)
|
||||
? [typeToConstructor(registry, Types)]
|
||||
: mapToTypeMap(registry, Types);
|
||||
: mapToTypeMap(registry, Types)
|
||||
);
|
||||
const [values, decodedLength] = isU8a(value)
|
||||
? decodeU8a(registry, value, Classes)
|
||||
: decodeTuple(registry, Classes, value);
|
||||
@@ -71,9 +81,15 @@ export class Tuple extends AbstractArray<Codec> implements ITuple<Codec[]> {
|
||||
}
|
||||
|
||||
public static with (Types: TupleTypes | TupleType): CodecClass<Tuple> {
|
||||
let definition: TupleCodecClass | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = (d: TupleCodecClass) =>
|
||||
definition = d;
|
||||
|
||||
return class extends Tuple {
|
||||
constructor (registry: Registry, value?: AnyTupleValue) {
|
||||
super(registry, Types, value);
|
||||
super(registry, Types, value, { definition, setDefinition });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -13,6 +13,15 @@ const MAX_LENGTH = 64 * 1024;
|
||||
|
||||
const l = logger('Vec');
|
||||
|
||||
interface Options<T> {
|
||||
definition?: CodecClass<T>;
|
||||
setDefinition?: (d: CodecClass<T>) => CodecClass<T>;
|
||||
}
|
||||
|
||||
function noopSetDefinition <T extends Codec> (d: CodecClass<T>): CodecClass<T> {
|
||||
return d;
|
||||
}
|
||||
|
||||
export function decodeVec<T extends Codec> (registry: Registry, Type: CodecClass<T>, value: Uint8Array | HexString | unknown[], length = -1): [T[], number, number] {
|
||||
if (Array.isArray(value)) {
|
||||
const result = new Array<T>(value.length);
|
||||
@@ -59,8 +68,8 @@ export function decodeVec<T extends Codec> (registry: Registry, Type: CodecClass
|
||||
export class Vec<T extends Codec> extends AbstractArray<T> {
|
||||
#Type: CodecClass<T>;
|
||||
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, value: Uint8Array | HexString | unknown[] = []) {
|
||||
const Clazz = typeToConstructor<T>(registry, Type);
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, value: Uint8Array | HexString | unknown[] = [], { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
|
||||
const Clazz = definition || setDefinition(typeToConstructor<T>(registry, Type));
|
||||
const [values, decodedLength] = decodeVec(registry, Clazz, value);
|
||||
|
||||
super(registry, values, decodedLength);
|
||||
@@ -69,9 +78,15 @@ export class Vec<T extends Codec> extends AbstractArray<T> {
|
||||
}
|
||||
|
||||
public static with<O extends Codec> (Type: CodecClass<O> | string): CodecClass<Vec<O>> {
|
||||
let definition: CodecClass<O> | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = <T> (d: CodecClass<T>) =>
|
||||
(definition = d as unknown as CodecClass<O>) as unknown as CodecClass<T>;
|
||||
|
||||
return class extends Vec<O> {
|
||||
constructor (registry: Registry, value?: any[]) {
|
||||
super(registry, Type, value);
|
||||
super(registry, Type, value, { definition, setDefinition });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,15 @@ import { AbstractArray } from '../abstract/AbstractArray';
|
||||
import { decodeU8aVec, typeToConstructor } from '../utils';
|
||||
import { decodeVec } from './Vec';
|
||||
|
||||
interface Options<T> {
|
||||
definition?: CodecClass<T>;
|
||||
setDefinition?: (d: CodecClass<T>) => CodecClass<T>;
|
||||
}
|
||||
|
||||
function noopSetDefinition <T extends Codec> (d: CodecClass<T>): CodecClass<T> {
|
||||
return d;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeVecFixed<T extends Codec> (registry: Registry, value: HexString | unknown[], Type: CodecClass<T>, length: number): [T[], number, number] {
|
||||
const [values, decodedLength, decodedLengthNoOffset] = decodeVec(registry, Type, value, length);
|
||||
@@ -31,8 +40,8 @@ function decodeVecFixed<T extends Codec> (registry: Registry, value: HexString |
|
||||
export class VecFixed<T extends Codec> extends AbstractArray<T> {
|
||||
#Type: CodecClass<T>;
|
||||
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, length: number, value: Uint8Array | HexString | unknown[] = [] as unknown[]) {
|
||||
const Clazz = typeToConstructor<T>(registry, Type);
|
||||
constructor (registry: Registry, Type: CodecClass<T> | string, length: number, value: Uint8Array | HexString | unknown[] = [] as unknown[], { definition, setDefinition = noopSetDefinition }: Options<T> = {}) {
|
||||
const Clazz = definition || setDefinition(typeToConstructor<T>(registry, Type));
|
||||
const [values,, decodedLengthNoOffset] = isU8a(value)
|
||||
? decodeU8aVec(registry, value, 0, Clazz, length)
|
||||
: decodeVecFixed(registry, value, Clazz, length);
|
||||
@@ -43,9 +52,15 @@ export class VecFixed<T extends Codec> extends AbstractArray<T> {
|
||||
}
|
||||
|
||||
public static with<O extends Codec> (Type: CodecClass<O> | string, length: number): CodecClass<VecFixed<O>> {
|
||||
let definition: CodecClass<O> | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = <T> (d: CodecClass<T>) =>
|
||||
(definition = d as unknown as CodecClass<O>) as unknown as CodecClass<T>;
|
||||
|
||||
return class extends VecFixed<O> {
|
||||
constructor (registry: Registry, value?: any[]) {
|
||||
super(registry, Type, length, value);
|
||||
super(registry, Type, length, value, { definition, setDefinition });
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
@@ -10,6 +10,15 @@ import { compareMap, decodeU8a, mapToTypeMap, typesToMap } from '../utils';
|
||||
|
||||
type TypesDef<T = Codec> = Record<string, string | CodecClass<T>>;
|
||||
|
||||
interface Options {
|
||||
definition?: CodecClassDef;
|
||||
setDefinition?: (d: CodecClassDef) => CodecClassDef;
|
||||
}
|
||||
|
||||
function noopSetDefinition (d: CodecClassDef): CodecClassDef {
|
||||
return d;
|
||||
}
|
||||
|
||||
/** @internal */
|
||||
function decodeStructFromObject (registry: Registry, Types: CodecClassDef, value: any, jsonMap: Map<string, string>): [Iterable<[string, Codec]>, number] {
|
||||
let jsonObj: Record<string, unknown> | undefined;
|
||||
@@ -100,8 +109,8 @@ export class Struct<
|
||||
|
||||
readonly #Types: CodecClassDef;
|
||||
|
||||
constructor (registry: Registry, Types: S, value?: V | Map<unknown, unknown> | unknown[] | HexString | null, jsonMap = new Map<string, string>()) {
|
||||
const typeMap = mapToTypeMap(registry, Types);
|
||||
constructor (registry: Registry, Types: S, value?: V | Map<unknown, unknown> | unknown[] | HexString | null, jsonMap = new Map<string, string>(), { definition, setDefinition = noopSetDefinition }: Options = {}) {
|
||||
const typeMap = definition || setDefinition(mapToTypeMap(registry, Types));
|
||||
const [decoded, decodedLength] = isU8a(value)
|
||||
? decodeU8a<Codec, [string, Codec]>(registry, value, typeMap, true)
|
||||
: isHex(value)
|
||||
@@ -121,9 +130,15 @@ export class Struct<
|
||||
public static with<S extends TypesDef> (Types: S, jsonMap?: Map<string, string>): CodecClass<Struct<S>> {
|
||||
const keys = Object.keys(Types);
|
||||
|
||||
let definition: CodecClassDef | undefined;
|
||||
|
||||
// eslint-disable-next-line no-return-assign
|
||||
const setDefinition = (d: CodecClassDef) =>
|
||||
definition = d;
|
||||
|
||||
return class extends Struct<S> {
|
||||
constructor (registry: Registry, value?: unknown) {
|
||||
super(registry, Types, value as HexString, jsonMap);
|
||||
super(registry, Types, value as HexString, jsonMap, { definition, setDefinition });
|
||||
|
||||
objectProperties(this, keys, (k) => this.get(k));
|
||||
}
|
||||
|
||||
@@ -205,7 +205,8 @@ describe('createType', (): void => {
|
||||
expect(balu32.bitLength()).toEqual(32);
|
||||
});
|
||||
|
||||
it('allows for re-registration of a type (affecting derives)', (): void => {
|
||||
it('allows for re-registration of a type (derived types)', (): void => {
|
||||
registry.clearCache();
|
||||
registry.register({
|
||||
Balance: 'u128',
|
||||
TestComplex: {
|
||||
@@ -224,7 +225,18 @@ describe('createType', (): void => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access,@typescript-eslint/no-unsafe-call
|
||||
expect(cmpDef.balance.bitLength()).toEqual(128);
|
||||
|
||||
registry.register({ Balance: 'u32' });
|
||||
registry.clearCache();
|
||||
registry.register({
|
||||
Balance: 'u32',
|
||||
TestComplex: {
|
||||
balance: 'Balance',
|
||||
// eslint-disable-next-line sort-keys
|
||||
accountId: 'AccountId',
|
||||
log: '(u64, u32)',
|
||||
// eslint-disable-next-line sort-keys
|
||||
fromSrml: 'Gas'
|
||||
}
|
||||
});
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
||||
const cmpu32: any = registry.createType('TestComplex');
|
||||
|
||||
Reference in New Issue
Block a user