diff --git a/dart/lib/flat_buffers.dart b/dart/lib/flat_buffers.dart index a389a8928..8b31f21b7 100644 --- a/dart/lib/flat_buffers.dart +++ b/dart/lib/flat_buffers.dart @@ -907,20 +907,34 @@ class Int8Reader extends Reader { int read(BufferContext bc, int offset) => bc._getInt8(offset); } -/// The reader of lists of objects. -/// -/// The returned unmodifiable lists lazily read objects on access. +/// The reader of lists of objects. Lazy by default - see [lazy]. class ListReader extends Reader> { final Reader _elementReader; - const ListReader(this._elementReader); + /// Enables lazy reading of the list + /// + /// If true, the returned unmodifiable list lazily reads objects on access. + /// Therefore, the underlying buffer must not change while accessing the list. + /// + /// If false, reads the whole list immediately on access. + final bool lazy; + + const ListReader(this._elementReader, {this.lazy = true}); @override int get size => _sizeofUint32; @override - List read(BufferContext bc, int offset) => - new _FbGenericList(_elementReader, bc, bc.derefObject(offset)); + List read(BufferContext bc, int offset) { + final listOffset = bc.derefObject(offset); + return lazy + ? _FbGenericList(_elementReader, bc, listOffset) + : List.generate( + bc.buffer.getUint32(listOffset, Endian.little), + (int index) => _elementReader.read( + bc, listOffset + size + _elementReader.size * index), + growable: true); + } } /// Object that can read a value at a [BufferContext]. diff --git a/dart/test/flat_buffers_test.dart b/dart/test/flat_buffers_test.dart index b536ecd84..ff77ff376 100644 --- a/dart/test/flat_buffers_test.dart +++ b/dart/test/flat_buffers_test.dart @@ -763,6 +763,28 @@ class ObjectAPITest { // final monster3 = monster2.unpack(); // MonsterT // expect(monster3.toString(), monster.toString()); } + + void test_Lists() { + // Ensure unpack() reads lists eagerly by reusing the same builder and + // overwriting data. Why: because standard reader reads lists lazily... + final fbb = Builder(); + + final object1 = example.TypeAliasesT(v8: [1, 2, 3], vf64: [5, 6]); + final data1 = fbb.finish(object1.pack(fbb)); + final object1Read = example.TypeAliases(data1).unpack(); + + // overwrite the original buffer by writing to the same builder + fbb.reset(); + final object2 = example.TypeAliasesT(v8: [7, 8, 9], vf64: [10, 11]); + final data2 = fbb.finish(object2.pack(fbb)); + final object2Read = example.TypeAliases(data2).unpack(); + + // this is fine even with lazy lists: + expect(object2.toString(), object2Read.toString()); + + // this fails with lazy lists: + expect(object1.toString(), object1Read.toString()); + } } class StringListWrapperImpl { diff --git a/dart/test/monster_test_my_game.example_generated.dart b/dart/test/monster_test_my_game.example_generated.dart index 36ada7d5f..ca4efd1c9 100644 --- a/dart/test/monster_test_my_game.example_generated.dart +++ b/dart/test/monster_test_my_game.example_generated.dart @@ -1117,15 +1117,15 @@ class Monster { mana: mana, hp: hp, name: name, - inventory: inventory, + inventory: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 14), color: color, testType: testType, test: test, test4: test4?.map((e) => e.unpack()).toList(), - testarrayofstring: testarrayofstring, + testarrayofstring: const fb.ListReader(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24), testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(), enemy: enemy?.unpack(), - testnestedflatbuffer: testnestedflatbuffer, + testnestedflatbuffer: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 30), testempty: testempty?.unpack(), testbool: testbool, testhashs32Fnv1: testhashs32Fnv1, @@ -1136,32 +1136,32 @@ class Monster { testhashu32Fnv1a: testhashu32Fnv1a, testhashs64Fnv1a: testhashs64Fnv1a, testhashu64Fnv1a: testhashu64Fnv1a, - testarrayofbools: testarrayofbools, + testarrayofbools: const fb.ListReader(const fb.BoolReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 52), testf: testf, testf2: testf2, testf3: testf3, - testarrayofstring2: testarrayofstring2, + testarrayofstring2: const fb.ListReader(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 60), testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(), - flex: flex, + flex: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 64), test5: test5?.map((e) => e.unpack()).toList(), - vectorOfLongs: vectorOfLongs, - vectorOfDoubles: vectorOfDoubles, + vectorOfLongs: const fb.ListReader(const fb.Int64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 68), + vectorOfDoubles: const fb.ListReader(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 70), parentNamespaceTest: parentNamespaceTest?.unpack(), vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(), singleWeakReference: singleWeakReference, - vectorOfWeakReferences: vectorOfWeakReferences, + vectorOfWeakReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 78), vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(), coOwningReference: coOwningReference, - vectorOfCoOwningReferences: vectorOfCoOwningReferences, + vectorOfCoOwningReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 84), nonOwningReference: nonOwningReference, - vectorOfNonOwningReferences: vectorOfNonOwningReferences, + vectorOfNonOwningReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 88), anyUniqueType: anyUniqueType, anyUnique: anyUnique, anyAmbiguousType: anyAmbiguousType, anyAmbiguous: anyAmbiguous, - vectorOfEnums: vectorOfEnums, + vectorOfEnums: const fb.ListReader(Color.reader, lazy: false).vTableGetNullable(_bc, _bcOffset, 98), signedEnum: signedEnum, - testrequirednestedflatbuffer: testrequirednestedflatbuffer, + testrequirednestedflatbuffer: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 102), scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList()); static int pack(fb.Builder fbBuilder, MonsterT? object) { @@ -1966,8 +1966,8 @@ class TypeAliases { u64: u64, f32: f32, f64: f64, - v8: v8, - vf64: vf64); + v8: const fb.ListReader(const fb.Int8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24), + vf64: const fb.ListReader(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 26)); static int pack(fb.Builder fbBuilder, TypeAliasesT? object) { if (object == null) return 0; diff --git a/src/idl_gen_dart.cpp b/src/idl_gen_dart.cpp index 7fefd87ba..4e78838aa 100644 --- a/src/idl_gen_dart.cpp +++ b/src/idl_gen_dart.cpp @@ -330,7 +330,8 @@ class DartGenerator : public BaseGenerator { std::string GenReaderTypeName(const Type &type, Namespace *current_namespace, const FieldDef &def, - bool parent_is_vector = false) { + bool parent_is_vector = false, + bool lazy = true) { if (type.base_type == BASE_TYPE_BOOL) { return "const " + _kFb + ".BoolReader()"; } else if (IsVector(type)) { @@ -338,7 +339,7 @@ class DartGenerator : public BaseGenerator { GenDartTypeName(type.VectorType(), current_namespace, def) + ">(" + GenReaderTypeName(type.VectorType(), current_namespace, def, true) + - ")"; + (lazy ? ")" : ", lazy: false)"); } else if (IsString(type)) { return "const " + _kFb + ".StringReader()"; } @@ -556,18 +557,32 @@ class DartGenerator : public BaseGenerator { std::string field_name = MakeCamel(field.name, false); if (!constructor_args.empty()) constructor_args += ",\n"; - constructor_args += " " + field_name + ": " + field_name; + constructor_args += " " + field_name + ": "; const Type &type = field.value.type; - bool isNullable = - getDefaultValue(field.value).empty() && !struct_def.fixed; + std::string defaultValue = getDefaultValue(field.value); + bool isNullable = defaultValue.empty() && !struct_def.fixed; std::string nullableValueAccessOperator = isNullable ? "?" : ""; if (type.base_type == BASE_TYPE_STRUCT) { - constructor_args += nullableValueAccessOperator + ".unpack()"; - } else if (type.base_type == BASE_TYPE_VECTOR && - type.VectorType().base_type == BASE_TYPE_STRUCT) { constructor_args += - nullableValueAccessOperator + ".map((e) => e.unpack()).toList()"; + field_name + nullableValueAccessOperator + ".unpack()"; + } else if (type.base_type == BASE_TYPE_VECTOR) { + if (type.VectorType().base_type == BASE_TYPE_STRUCT) { + constructor_args += field_name + nullableValueAccessOperator + + ".map((e) => e.unpack()).toList()"; + } else { + constructor_args += + GenReaderTypeName(field.value.type, struct_def.defined_namespace, + field, false, false); + constructor_args += ".vTableGet"; + std::string offset = NumToString(field.value.offset); + constructor_args += + isNullable + ? "Nullable(_bc, _bcOffset, " + offset + ")" + : "(_bc, _bcOffset, " + offset + ", " + defaultValue + ")"; + } + } else { + constructor_args += field_name; } } diff --git a/tests/monster_extra_my_game_generated.dart b/tests/monster_extra_my_game_generated.dart index 7afa43299..b6e9b77fd 100644 --- a/tests/monster_extra_my_game_generated.dart +++ b/tests/monster_extra_my_game_generated.dart @@ -44,8 +44,8 @@ class MonsterExtra { f1: f1, f2: f2, f3: f3, - dvec: dvec, - fvec: fvec); + dvec: const fb.ListReader(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 20), + fvec: const fb.ListReader(const fb.Float32Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 22)); static int pack(fb.Builder fbBuilder, MonsterExtraT? object) { if (object == null) return 0; diff --git a/tests/monster_test_my_game.example_generated.dart b/tests/monster_test_my_game.example_generated.dart index 36ada7d5f..ca4efd1c9 100644 --- a/tests/monster_test_my_game.example_generated.dart +++ b/tests/monster_test_my_game.example_generated.dart @@ -1117,15 +1117,15 @@ class Monster { mana: mana, hp: hp, name: name, - inventory: inventory, + inventory: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 14), color: color, testType: testType, test: test, test4: test4?.map((e) => e.unpack()).toList(), - testarrayofstring: testarrayofstring, + testarrayofstring: const fb.ListReader(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24), testarrayoftables: testarrayoftables?.map((e) => e.unpack()).toList(), enemy: enemy?.unpack(), - testnestedflatbuffer: testnestedflatbuffer, + testnestedflatbuffer: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 30), testempty: testempty?.unpack(), testbool: testbool, testhashs32Fnv1: testhashs32Fnv1, @@ -1136,32 +1136,32 @@ class Monster { testhashu32Fnv1a: testhashu32Fnv1a, testhashs64Fnv1a: testhashs64Fnv1a, testhashu64Fnv1a: testhashu64Fnv1a, - testarrayofbools: testarrayofbools, + testarrayofbools: const fb.ListReader(const fb.BoolReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 52), testf: testf, testf2: testf2, testf3: testf3, - testarrayofstring2: testarrayofstring2, + testarrayofstring2: const fb.ListReader(const fb.StringReader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 60), testarrayofsortedstruct: testarrayofsortedstruct?.map((e) => e.unpack()).toList(), - flex: flex, + flex: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 64), test5: test5?.map((e) => e.unpack()).toList(), - vectorOfLongs: vectorOfLongs, - vectorOfDoubles: vectorOfDoubles, + vectorOfLongs: const fb.ListReader(const fb.Int64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 68), + vectorOfDoubles: const fb.ListReader(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 70), parentNamespaceTest: parentNamespaceTest?.unpack(), vectorOfReferrables: vectorOfReferrables?.map((e) => e.unpack()).toList(), singleWeakReference: singleWeakReference, - vectorOfWeakReferences: vectorOfWeakReferences, + vectorOfWeakReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 78), vectorOfStrongReferrables: vectorOfStrongReferrables?.map((e) => e.unpack()).toList(), coOwningReference: coOwningReference, - vectorOfCoOwningReferences: vectorOfCoOwningReferences, + vectorOfCoOwningReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 84), nonOwningReference: nonOwningReference, - vectorOfNonOwningReferences: vectorOfNonOwningReferences, + vectorOfNonOwningReferences: const fb.ListReader(const fb.Uint64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 88), anyUniqueType: anyUniqueType, anyUnique: anyUnique, anyAmbiguousType: anyAmbiguousType, anyAmbiguous: anyAmbiguous, - vectorOfEnums: vectorOfEnums, + vectorOfEnums: const fb.ListReader(Color.reader, lazy: false).vTableGetNullable(_bc, _bcOffset, 98), signedEnum: signedEnum, - testrequirednestedflatbuffer: testrequirednestedflatbuffer, + testrequirednestedflatbuffer: const fb.ListReader(const fb.Uint8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 102), scalarKeySortedTables: scalarKeySortedTables?.map((e) => e.unpack()).toList()); static int pack(fb.Builder fbBuilder, MonsterT? object) { @@ -1966,8 +1966,8 @@ class TypeAliases { u64: u64, f32: f32, f64: f64, - v8: v8, - vf64: vf64); + v8: const fb.ListReader(const fb.Int8Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 24), + vf64: const fb.ListReader(const fb.Float64Reader(), lazy: false).vTableGetNullable(_bc, _bcOffset, 26)); static int pack(fb.Builder fbBuilder, TypeAliasesT? object) { if (object == null) return 0;