diff --git a/yarn-project/foundation/src/json-rpc/class_converter.ts b/yarn-project/foundation/src/json-rpc/class_converter.ts index 2be9dab1d5b5..441535df8373 100644 --- a/yarn-project/foundation/src/json-rpc/class_converter.ts +++ b/yarn-project/foundation/src/json-rpc/class_converter.ts @@ -103,7 +103,6 @@ export type ClassEncoding = 'string' | 'object'; */ export class ClassConverter { private toClass = new Map(); - private toName = new Map(); /** * Create a class converter from a table of classes. @@ -140,7 +139,6 @@ export class ClassConverter { (class_ as StringIOClass)['fromString'] || (class_ as ObjIOClass)['fromJSON'], `Class ${type} must define a fromString() OR fromJSON() static method.`, ); - this.toName.set(class_, [type, encoding]); this.toClass.set(type, [class_, encoding]); } @@ -153,12 +151,13 @@ export class ClassConverter { return this.toClass.has(type); } /** - * Is this class object registered? + * Is this class object registered under its constructor name? * @param obj - The class object. * @returns If it is a registered class. */ isRegisteredClass(obj: any) { - return this.toName.has(obj); + const name = obj.prototype.constructor.name; + return this.isRegisteredClassName(name); } /** * Convert a JSON-like object to a class object. @@ -182,10 +181,11 @@ export class ClassConverter { * @returns The class object. */ toJsonObj(classObj: any): JsonEncodedClass | StringEncodedClass { - const result = this.toName.get(classObj.constructor); + const type = classObj.constructor.name; + const result = this.toClass.get(type); assert(result, `Could not find class in lookup.`); - const [type, encoding] = result; + const [_class, encoding] = result; const data = encoding === 'string' ? classObj.toString() : classObj.toJSON(); - return { type: type!, data }; + return { type, data }; } } diff --git a/yarn-project/foundation/src/json-rpc/convert.test.ts b/yarn-project/foundation/src/json-rpc/convert.test.ts index 4927b9c68a08..664b7474ec03 100644 --- a/yarn-project/foundation/src/json-rpc/convert.test.ts +++ b/yarn-project/foundation/src/json-rpc/convert.test.ts @@ -2,6 +2,8 @@ import { Buffer } from 'buffer'; import { ClassConverter } from './class_converter.js'; import { convertBigintsInObj, convertFromJsonObj, convertToJsonObj } from './convert.js'; +import { ToStringClass as ToStringClassA } from './fixtures/class_a.js'; +import { ToStringClass as ToStringClassB } from './fixtures/class_b.js'; import { TestNote } from './fixtures/test_state.js'; const TEST_BASE64 = 'YmFzZTY0IGRlY29kZXI='; @@ -24,3 +26,23 @@ test('does not convert a string', () => { expect(convertBigintsInObj('hello')).toEqual('hello'); expect(convertBigintsInObj({ msg: 'hello' })).toEqual({ msg: 'hello' }); }); + +test('converts a registered class', () => { + const cc = new ClassConverter({ ToStringClass: ToStringClassA }); + const obj = { content: new ToStringClassA('a', 'b') }; + const serialised = convertToJsonObj(cc, obj); + const deserialised = convertFromJsonObj(cc, serialised) as { content: ToStringClassA }; + expect(deserialised.content).toBeInstanceOf(ToStringClassA); + expect(deserialised.content.x).toEqual('a'); + expect(deserialised.content.y).toEqual('b'); +}); + +test('converts a class by name', () => { + const cc = new ClassConverter({ ToStringClass: ToStringClassA }); + const obj = { content: new ToStringClassB('a', 'b') }; + const serialised = convertToJsonObj(cc, obj); + const deserialised = convertFromJsonObj(cc, serialised) as { content: ToStringClassA }; + expect(deserialised.content).toBeInstanceOf(ToStringClassA); + expect(deserialised.content.x).toEqual('a'); + expect(deserialised.content.y).toEqual('b'); +}); diff --git a/yarn-project/foundation/src/json-rpc/convert.ts b/yarn-project/foundation/src/json-rpc/convert.ts index acbfd2e544d2..5ebbaf8bb89f 100644 --- a/yarn-project/foundation/src/json-rpc/convert.ts +++ b/yarn-project/foundation/src/json-rpc/convert.ts @@ -76,8 +76,8 @@ export function convertFromJsonObj(cc: ClassConverter, obj: any): any { return newObj; } // Throw if this is a non-primitive class that was not registered - if (typeof obj === 'object' && obj.constructor !== Object) { - throw new Error(`Object ${obj.constructor.name} not registered for deserialisation`); + if (typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.getPrototypeOf({})) { + throw new Error(`Class ${obj.constructor.name} not registered for deserialisation`); } // Leave alone, assume JSON primitive return obj; @@ -122,7 +122,7 @@ export function convertToJsonObj(cc: ClassConverter, obj: any): any { return newObj; } // Throw if this is a non-primitive class that was not registered - if (typeof obj === 'object' && obj.constructor !== Object) { + if (typeof obj === 'object' && Object.getPrototypeOf(obj) === Object.getPrototypeOf({})) { throw new Error(`Object ${obj.constructor.name} not registered for serialisation`); } // Leave alone, assume JSON primitive diff --git a/yarn-project/foundation/src/json-rpc/fixtures/class_a.ts b/yarn-project/foundation/src/json-rpc/fixtures/class_a.ts new file mode 100644 index 000000000000..58bb9a0cc193 --- /dev/null +++ b/yarn-project/foundation/src/json-rpc/fixtures/class_a.ts @@ -0,0 +1,15 @@ +/** + * Test class for testing string converter. + */ +export class ToStringClass { + constructor(/** A value */ public readonly x: string, /** Another value */ public readonly y: string) {} + + toString(): string { + return [this.x, this.y].join('-'); + } + + static fromString(value: string) { + const [x, y] = value.split('-'); + return new ToStringClass(x, y); + } +} diff --git a/yarn-project/foundation/src/json-rpc/fixtures/class_b.ts b/yarn-project/foundation/src/json-rpc/fixtures/class_b.ts new file mode 100644 index 000000000000..58bb9a0cc193 --- /dev/null +++ b/yarn-project/foundation/src/json-rpc/fixtures/class_b.ts @@ -0,0 +1,15 @@ +/** + * Test class for testing string converter. + */ +export class ToStringClass { + constructor(/** A value */ public readonly x: string, /** Another value */ public readonly y: string) {} + + toString(): string { + return [this.x, this.y].join('-'); + } + + static fromString(value: string) { + const [x, y] = value.split('-'); + return new ToStringClass(x, y); + } +}