Skip to content

Commit 29fad01

Browse files
authored
fix(core/cbor): NumericValue typecheck (#1673)
1 parent 17b29f2 commit 29fad01

File tree

5 files changed

+115
-4
lines changed

5 files changed

+115
-4
lines changed

.changeset/curly-fishes-tease.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@smithy/core": patch
3+
---
4+
5+
fix NumericValue typecheck

packages/core/src/submodules/cbor/byte-printer.ts

Lines changed: 38 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,42 @@
44
* @deprecated for testing only, do not use in runtime.
55
*/
66
export function printBytes(bytes: Uint8Array) {
7-
return [...bytes].map((n) => ("0".repeat(8) + n.toString(2)).slice(-8) + ` (${n})`);
7+
return [...bytes].map((n) => {
8+
const pad = (num: number) => ("0".repeat(8) + num.toString(2)).slice(-8);
9+
const b = pad(n);
10+
const [maj, min] = [b.slice(0, 3), b.slice(3)];
11+
12+
let dmaj: string = "";
13+
14+
switch (maj) {
15+
case "000":
16+
dmaj = "0 - Uint64";
17+
break;
18+
case "001":
19+
dmaj = "1 - Neg Uint64";
20+
break;
21+
case "010":
22+
dmaj = "2 - unstructured bytestring";
23+
break;
24+
case "011":
25+
dmaj = "3 - utf8 string";
26+
break;
27+
case "100":
28+
dmaj = "4 - list";
29+
break;
30+
case "101":
31+
dmaj = "5 - map";
32+
break;
33+
case "110":
34+
dmaj = "6 - tag";
35+
break;
36+
case "111":
37+
dmaj = "7 - special";
38+
break;
39+
default:
40+
dmaj = String(parseInt(maj, 2));
41+
}
42+
43+
return `${maj}_${min} (${dmaj}, ${parseInt(min, 2)})`;
44+
});
845
}

packages/core/src/submodules/cbor/cbor.spec.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import JSONbig from "json-bigint";
55
import * as path from "path";
66
import { describe, expect, test as it } from "vitest";
77

8+
import { printBytes } from "./byte-printer";
89
import { cbor } from "./cbor";
910
import { bytesToFloat16 } from "./cbor-decode";
1011
import { tagSymbol } from "./cbor-types";
@@ -286,7 +287,7 @@ describe("cbor", () => {
286287
expect(deserialized).toEqual(bigInt);
287288
});
288289

289-
it.skip("should round-trip NumericValue to major 6 with tag 4", () => {
290+
it("should round-trip NumericValue to major 6 with tag 4", () => {
290291
for (const bigDecimal of [
291292
"10000000000000000000000054.321",
292293
"1000000000000000000000000000000000054.134134321",
@@ -310,6 +311,15 @@ describe("cbor", () => {
310311
expect(deserialized).toEqual(nv);
311312
expect(deserialized.string).toEqual(nv.string);
312313
}
314+
315+
const bigDecimal = nv("0");
316+
expect(bigDecimal).toBeInstanceOf(NumericValue);
317+
expect(printBytes(cbor.serialize(bigDecimal))).toEqual([
318+
"110_00100 (6 - tag, 4)",
319+
"100_00010 (4 - list, 2)",
320+
"000_00000 (0 - Uint64, 0)",
321+
"000_00000 (0 - Uint64, 0)",
322+
]);
313323
});
314324

315325
it("should round-trip sequences of big numbers", () => {

packages/core/src/submodules/serde/value/NumericValue.spec.ts

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,4 +16,59 @@ describe(NumericValue.name, () => {
1616
expect(() => nv("-10.1")).not.toThrow();
1717
expect(() => nv("-.101")).not.toThrow();
1818
});
19+
20+
it("has a custom instanceof check", () => {
21+
const isInstance = [
22+
nv("0"),
23+
nv("-0.00"),
24+
new NumericValue("0", "bigDecimal"),
25+
new NumericValue("-0.00", "bigDecimal"),
26+
{
27+
string: "abcd",
28+
type: "bigDecimal",
29+
constructor: {
30+
name: "_NumericValue",
31+
},
32+
},
33+
(() => {
34+
const x = {};
35+
Object.setPrototypeOf(x, NumericValue.prototype);
36+
return x;
37+
})(),
38+
(() => {
39+
function F() {}
40+
F.prototype = Object.create(NumericValue.prototype);
41+
// @ts-ignore
42+
return new F();
43+
})(),
44+
(() => {
45+
return new (class extends NumericValue {})("0", "bigDecimal");
46+
})(),
47+
] as unknown[];
48+
49+
const isNotInstance = [
50+
BigInt(0),
51+
"-0.00",
52+
{
53+
string: "abcd",
54+
type: "bigDecimal",
55+
constructor: {
56+
name: "_NumericValue_",
57+
},
58+
},
59+
(() => {
60+
const x = {};
61+
Object.setPrototypeOf(x, NumericValue);
62+
return x;
63+
})(),
64+
] as unknown[];
65+
66+
for (const instance of isInstance) {
67+
expect(instance).toBeInstanceOf(NumericValue);
68+
}
69+
70+
for (const instance of isNotInstance) {
71+
expect(instance).not.toBeInstanceOf(NumericValue);
72+
}
73+
});
1974
});

packages/core/src/submodules/serde/value/NumericValue.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -58,11 +58,15 @@ export class NumericValue {
5858
return false;
5959
}
6060
const _nv = object as NumericValue;
61-
const prototypeMatch = NumericValue.prototype.isPrototypeOf(object.constructor?.prototype);
61+
const prototypeMatch = NumericValue.prototype.isPrototypeOf(object);
6262
if (prototypeMatch) {
6363
return prototypeMatch;
6464
}
65-
if (typeof _nv.string === "string" && typeof _nv.type === "string" && _nv.constructor?.name === "NumericValue") {
65+
if (
66+
typeof _nv.string === "string" &&
67+
typeof _nv.type === "string" &&
68+
_nv.constructor?.name?.endsWith("NumericValue")
69+
) {
6670
return true;
6771
}
6872
return prototypeMatch;

0 commit comments

Comments
 (0)