From c6f5bb55e149fb77db9dbcdf477ee7635b032a68 Mon Sep 17 00:00:00 2001 From: Eddy Ashton Date: Mon, 10 Oct 2022 08:36:12 +0100 Subject: [PATCH] Enforce type of arg in TS DataConverter.convert (#4327) --- js/ccf-app/src/converters.ts | 36 ++++++++++++++++++++++++++++++++++++ js/ccf-app/test/kv.test.ts | 32 +++++++++++++++++++++++++++++++- 2 files changed, 67 insertions(+), 1 deletion(-) diff --git a/js/ccf-app/src/converters.ts b/js/ccf-app/src/converters.ts index 47f1c9c7ec..6e2332c23d 100644 --- a/js/ccf-app/src/converters.ts +++ b/js/ccf-app/src/converters.ts @@ -32,8 +32,33 @@ export interface DataConverter { decode(arr: ArrayBuffer): T; } +function checkBoolean(val: any) { + if (typeof val !== "boolean") { + throw new TypeError(`Value ${val} is not a boolean`); + } +} + +function checkNumber(val: any) { + if (typeof val !== "number") { + throw new TypeError(`Value ${val} is not a number`); + } +} + +function checkBigInt(val: any) { + if (typeof val !== "bigint") { + throw new TypeError(`Value ${val} is not a bigint`); + } +} + +function checkString(val: any) { + if (typeof val !== "string") { + throw new TypeError(`Value ${val} is not a string`); + } +} + class BoolConverter implements DataConverter { encode(val: boolean): ArrayBuffer { + checkBoolean(val); const buf = new ArrayBuffer(1); new DataView(buf).setUint8(0, val ? 1 : 0); return buf; @@ -44,6 +69,7 @@ class BoolConverter implements DataConverter { } class Int8Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < -128 || val > 127) { throw new RangeError("value is not within int8 range"); } @@ -57,6 +83,7 @@ class Int8Converter implements DataConverter { } class Uint8Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < 0 || val > 255) { throw new RangeError("value is not within uint8 range"); } @@ -70,6 +97,7 @@ class Uint8Converter implements DataConverter { } class Int16Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < -32768 || val > 32767) { throw new RangeError("value is not within int16 range"); } @@ -83,6 +111,7 @@ class Int16Converter implements DataConverter { } class Uint16Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < 0 || val > 65535) { throw new RangeError("value is not within uint16 range"); } @@ -96,6 +125,7 @@ class Uint16Converter implements DataConverter { } class Int32Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < -2147483648 || val > 2147483647) { throw new RangeError("value is not within int32 range"); } @@ -109,6 +139,7 @@ class Int32Converter implements DataConverter { } class Uint32Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); if (val < 0 || val > 4294967295) { throw new RangeError("value is not within uint32 range"); } @@ -122,6 +153,7 @@ class Uint32Converter implements DataConverter { } class Int64Converter implements DataConverter { encode(val: bigint): ArrayBuffer { + checkBigInt(val); const buf = new ArrayBuffer(8); new DataView(buf).setBigInt64(0, val, true); return buf; @@ -132,6 +164,7 @@ class Int64Converter implements DataConverter { } class Uint64Converter implements DataConverter { encode(val: bigint): ArrayBuffer { + checkBigInt(val); const buf = new ArrayBuffer(8); new DataView(buf).setBigUint64(0, val, true); return buf; @@ -142,6 +175,7 @@ class Uint64Converter implements DataConverter { } class Float32Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); const buf = new ArrayBuffer(4); new DataView(buf).setFloat32(0, val, true); return buf; @@ -152,6 +186,7 @@ class Float32Converter implements DataConverter { } class Float64Converter implements DataConverter { encode(val: number): ArrayBuffer { + checkNumber(val); const buf = new ArrayBuffer(8); new DataView(buf).setFloat64(0, val, true); return buf; @@ -162,6 +197,7 @@ class Float64Converter implements DataConverter { } class StringConverter implements DataConverter { encode(val: string): ArrayBuffer { + checkString(val); return ccf.strToBuf(val); } decode(buf: ArrayBuffer): string { diff --git a/js/ccf-app/test/kv.test.ts b/js/ccf-app/test/kv.test.ts index 96e160c58a..9ff4e6e701 100644 --- a/js/ccf-app/test/kv.test.ts +++ b/js/ccf-app/test/kv.test.ts @@ -17,10 +17,13 @@ describe("typedKv", function () { const val = 65535; it("basic", function () { + assert.isFalse(foo.has(key)); + assert.isFalse(foo.has(key2)); assert.equal(foo.get(key), undefined); foo.set(key, val); assert.equal(foo.get(key), val); assert.isTrue(foo.has(key)); + assert.isFalse(foo.has(key2)); let found = false; foo.forEach((v, k) => { if (k == key && v == val) { @@ -29,7 +32,8 @@ describe("typedKv", function () { }); assert.isTrue(found); foo.delete(key); - assert.isNotTrue(foo.has(key)); + assert.isFalse(foo.has(key)); + assert.isFalse(foo.has(key2)); assert.equal(foo.get(key), undefined); }); @@ -59,3 +63,29 @@ describe("typedKv", function () { assert.equal(foo.size, 0); }); }); + +class TypeErasedKvMap { + constructor(private map: kv.TypedKvMap) {} + + has(key: any): boolean { + return this.map.has(key); + } + get(key: any): V | undefined { + return this.map.get(key); + } + set(key: any, value: V) { + this.map.set(key, value); + } +} + +describe("erased types", function () { + const bar = new TypeErasedKvMap(kv.typedKv("bar", conv.int32, conv.uint16)); + const key = "baz"; + const val = 65535; + + it("basic", function () { + assert.throws(() => bar.has(key), `${key} is not a number`); + assert.throws(() => bar.get(key), `${key} is not a number`); + assert.throws(() => bar.set(key, val), `${key} is not a number`); + }); +});