Skip to content

Commit b98e030

Browse files
committed
fix(bson): fix bug with serializer/parsing nested arrays or maps
1 parent 3f0d084 commit b98e030

File tree

3 files changed

+71
-25
lines changed

3 files changed

+71
-25
lines changed

packages/bson/src/bson-jit-parser.ts

Lines changed: 9 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19-
import {ClassSchema, getClassSchema, PropertySchema} from '@deepkit/type';
19+
import {ClassSchema, getClassSchema, PropertySchema, reserveVariable} from '@deepkit/type';
2020
import {BSON_BINARY_SUBTYPE_UUID, BSON_DATA_ARRAY, BSON_DATA_BINARY, BSON_DATA_DATE, BSON_DATA_NULL, BSON_DATA_OBJECT, digitByteSize, moment} from './utils';
2121
import {ClassType} from '@deepkit/core';
2222
import {BaseParser, ParserV2} from './bson-parser';
@@ -79,6 +79,7 @@ function createPropertyConverter(setter: string, property: PropertySchema, conte
7979
`;
8080
} else if (property.isArray) {
8181
context.set('digitByteSize', digitByteSize);
82+
const v = reserveVariable(context, 'v');
8283

8384
return `
8485
if (elementType === ${BSON_DATA_ARRAY}) {
@@ -91,16 +92,17 @@ function createPropertyConverter(setter: string, property: PropertySchema, conte
9192
//arrays are represented as objects, so we skip the key name
9293
parser.seek(digitByteSize(i));
9394
94-
let v = undefined;
95-
${createPropertyConverter(`v`, property.getSubType(), context, property)}
96-
${setter}.push(v);
95+
let ${v} = undefined;
96+
${createPropertyConverter(v, property.getSubType(), context, property)}
97+
${setter}.push(${v});
9798
}
98-
continue;
9999
} else {
100100
${nullOrSeek}
101101
}
102102
`;
103103
} else if (property.isMap) {
104+
const name = reserveVariable(context, 'propertyName');
105+
104106
return `
105107
if (elementType === ${BSON_DATA_OBJECT}) {
106108
${setter} = {};
@@ -109,11 +111,10 @@ function createPropertyConverter(setter: string, property: PropertySchema, conte
109111
const elementType = parser.eatByte();
110112
if (elementType === 0) break;
111113
112-
const name = parser.eatObjectPropertyName();
114+
${name} = parser.eatObjectPropertyName();
113115
114-
${createPropertyConverter(`${setter}[name]`, property.getSubType(), context)}
116+
${createPropertyConverter(`${setter}[${name}]`, property.getSubType(), context)}
115117
}
116-
continue;
117118
} else {
118119
${nullOrSeek}
119120
}

packages/bson/src/bson-serialize.ts

Lines changed: 26 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
* along with this program. If not, see <http://www.gnu.org/licenses/>.
1717
*/
1818

19-
import {ClassSchema, getClassSchema, getGlobalStore, JitStack, PropertySchema} from '@deepkit/type';
19+
import {ClassSchema, getClassSchema, getGlobalStore, JitStack, PropertySchema, reserveVariable} from '@deepkit/type';
2020
import {ClassType, isArray, isObject, toFastProperties} from '@deepkit/core';
2121
import {seekElementSize} from './continuation';
2222
import {
@@ -167,24 +167,26 @@ function getPropertySizer(context: Map<string, any>, property: PropertySchema, a
167167

168168
if (property.type === 'array') {
169169
context.set('digitByteSize', digitByteSize);
170+
const i = reserveVariable(context, 'i');
170171
code = `
171172
size += 4; //array size
172-
for (let i = 0; i < ${accessor}.length; i++) {
173+
for (let ${i} = 0; ${i} < ${accessor}.length; ${i}++) {
173174
size += 1; //element type
174-
size += digitByteSize(i); //element name
175-
${getPropertySizer(context, property.getSubType(), `${accessor}[i]`, jitStack)}
175+
size += digitByteSize(${i}); //element name
176+
${getPropertySizer(context, property.getSubType(), `${accessor}[${i}]`, jitStack)}
176177
}
177178
size += 1; //null
178179
`;
179180
} else if (property.type === 'map') {
180181
context.set('stringByteLength', stringByteLength);
182+
const i = reserveVariable(context, 'i');
181183
code = `
182184
size += 4; //object size
183-
for (let i in ${accessor}) {
184-
if (!${accessor}.hasOwnProperty(i)) continue;
185+
for (${i} in ${accessor}) {
186+
if (!${accessor}.hasOwnProperty(${i})) continue;
185187
size += 1; //element type
186-
size += stringByteLength(i) + 1; //element name + null
187-
${getPropertySizer(context, property.getSubType(), `${accessor}[i]`, jitStack)}
188+
size += stringByteLength(${i}) + 1; //element name + null;
189+
${getPropertySizer(context, property.getSubType(), `${accessor}[${i}]`, jitStack)}
188190
}
189191
size += 1; //null
190192
`;
@@ -249,10 +251,15 @@ export function createBSONSizer(classSchema: ClassSchema, jitStack: JitStack = n
249251
}
250252
`;
251253

252-
const compiled = new Function('Buffer', 'seekElementSize', ...context.keys(), functionCode);
253-
const fn = compiled.bind(undefined, Buffer, seekElementSize, ...context.values())();
254-
prepared(fn);
255-
return fn;
254+
try {
255+
const compiled = new Function('Buffer', 'seekElementSize', ...context.keys(), functionCode);
256+
const fn = compiled.bind(undefined, Buffer, seekElementSize, ...context.values())();
257+
prepared(fn);
258+
return fn;
259+
} catch (error) {
260+
console.log('Error compiling BSON sizer', functionCode);
261+
throw error;
262+
}
256263
}
257264

258265
export class Writer {
@@ -667,30 +674,32 @@ function getPropertySerializerCode(
667674
} else if (property.type === 'number') {
668675
code = numberSerializer();
669676
} else if (property.type === 'array') {
677+
const i = reserveVariable(context, 'i');
670678
code = `
671679
writer.writeByte(${BSON_DATA_ARRAY});
672680
${nameWriter}
673681
const start = writer.offset;
674682
writer.offset += 4; //size
675683
676-
for (let i = 0; i < ${accessor}.length; i++) {
684+
for (let ${i} = 0; ${i} < ${accessor}.length; ${i}++) {
677685
//${property.getSubType().type}
678-
${getPropertySerializerCode(property.getSubType(), context, `${accessor}[i]`, jitStack, `''+i`)}
686+
${getPropertySerializerCode(property.getSubType(), context, `${accessor}[${i}]`, jitStack, `''+${i}`)}
679687
}
680688
writer.writeNull();
681689
writer.writeDelayedSize(writer.offset - start, start);
682690
`;
683691
} else if (property.type === 'map') {
692+
const i = reserveVariable(context, 'i');
684693
code = `
685694
writer.writeByte(${BSON_DATA_OBJECT});
686695
${nameWriter}
687696
const start = writer.offset;
688697
writer.offset += 4; //size
689698
690-
for (let i in ${accessor}) {
691-
if (!${accessor}.hasOwnProperty(i)) continue;
699+
for (let ${i} in ${accessor}) {
700+
if (!${accessor}.hasOwnProperty(${i})) continue;
692701
//${property.getSubType().type}
693-
${getPropertySerializerCode(property.getSubType(), context, `${accessor}[i]`, jitStack, `i`)}
702+
${getPropertySerializerCode(property.getSubType(), context, `${accessor}[${i}]`, jitStack, `${i}`)}
694703
}
695704
writer.writeNull();
696705
writer.writeDelayedSize(writer.offset - start, start);

packages/bson/tests/bson-serialize.spec.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -993,3 +993,39 @@ test('typed any and undefined', () => {
993993
expect(back.data.$set).toEqual({});
994994
expect(back.data.$inc).toEqual(undefined);
995995
});
996+
997+
test('test map map', () => {
998+
const schema = t.schema({
999+
data: t.map(t.map(t.string)),
1000+
});
1001+
1002+
const message = jsonSerializer.for(schema).deserialize({
1003+
data: {foo: {bar: 'abc'}},
1004+
});
1005+
1006+
const size = getBSONSizer(schema)(message);
1007+
expect(size).toBe(calculateObjectSize(message));
1008+
1009+
const bson = getBSONSerializer(schema)(message);
1010+
expect(bson).toEqual(serialize(message));
1011+
1012+
expect(getBSONDecoder(schema)(bson)).toEqual(message);
1013+
});
1014+
1015+
test('test array array', () => {
1016+
const schema = t.schema({
1017+
data: t.array(t.array(t.string)),
1018+
});
1019+
1020+
const message = jsonSerializer.for(schema).deserialize({
1021+
data: [['abc']],
1022+
});
1023+
1024+
const size = getBSONSizer(schema)(message);
1025+
expect(size).toBe(calculateObjectSize(message));
1026+
1027+
const bson = getBSONSerializer(schema)(message);
1028+
1029+
expect(bson).toEqual(serialize(message));
1030+
expect(getBSONDecoder(schema)(bson)).toEqual(message);
1031+
});

0 commit comments

Comments
 (0)