From eff29f12d6fcacd19853ae738b511b1a9a1ef745 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Patryk=20Wr=C3=B3bel?= <183546751+pwrobeldev@users.noreply.github.com> Date: Tue, 14 Jan 2025 07:29:01 +0100 Subject: [PATCH] Dart: allow non-const constructible defaults to work with 'PositionalDefaults' (#1632) ------ Motivation ------ When fields with default values were defined in a struct and their types did not provide 'const' constructors, when such fields were used in combination with 'PositionalDefaults', then the generated code did not compile. ------ Designed solution ------ In order to be able to generate the code, which compiles when 'PositionalDefaults' annotation is used together with default values of types that do not provide const constructors, the optional values have been used. When Gluecodium encounters a type that does not provide const constructor it makes it optional in positional defaults constructor and uses null as default. Later, on initializer list it checks if user provided value different than null and if not, then initializes the field with the actual default value. This way the limitation related to const constructors is surpassed. However, if the user wants to have nullable type that does not provide const constructor and uses default value different than null, then the behavior may be surprising. Therefore, the validator was implemented for Dart to prevent users from such usage. ------ Contents of change ------ 1. New smoke and functional tests, which confirm invalid behavior. 2. Implementation of designed fix. 3. New functional tests, which confirm that the fix works as expected. 4. Validator of default values to prevent unexpected behavior + unit tests. Signed-off-by: Patryk Wrobel <183546751+pwrobeldev@users.noreply.github.com> --- CHANGELOG.md | 1 + docs/lime_attributes.md | 6 + .../functional/dart/test/Defaults_test.dart | 43 +++ .../input/lime/PositionalDefaults.lime | 30 ++ .../dart/DartDefaultValuesValidator.kt | 76 +++++ .../generator/dart/DartGenerator.kt | 6 +- .../generator/dart/DartGeneratorPredicates.kt | 19 ++ .../dart/DartStructConstructors.mustache | 23 +- .../dart/DartDefaultValuesValidatorTest.kt | 208 ++++++++++++++ .../input/DartPositionalDefaults.lime | 29 ++ ...ult_struct_with_custom_structs_fields.dart | 261 ++++++++++++++++++ 11 files changed, 698 insertions(+), 4 deletions(-) create mode 100644 gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidator.kt create mode 100644 gluecodium/src/test/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidatorTest.kt create mode 100644 gluecodium/src/test/resources/smoke/defaults/output/dart/lib/src/smoke/pos_default_struct_with_custom_structs_fields.dart diff --git a/CHANGELOG.md b/CHANGELOG.md index bf75361f46..80e7cc6444 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ## Unreleased ### Bug fixes: + * Dart: fixed a bug related to compilation error caused by usage of `@PositionalDefaults` and default value for a field that uses type, which does not provide const constructor. * Dart: disabled generation of `invalid_use_of_visible_for_testing_member` warning from the generated code for a hierarchy of classes. * Dart: removed generation of redundant import for constants declared with external types. The redundant import caused linter warnings. * Dart: removed generation of redundant `default` clause in exhaustive switch-cases for enumerations. The redundant `default` caused linter warnings. diff --git a/docs/lime_attributes.md b/docs/lime_attributes.md index de1ce0c892..3ddfbf285f 100644 --- a/docs/lime_attributes.md +++ b/docs/lime_attributes.md @@ -131,6 +131,12 @@ name was defined through command-line parameters. If the tag is not present, the positional parameters in Dart. Can only be applied to a struct that has at least one field with a default value. The positional defaults constructor will be generated with a `@Deprecated` annotation, if _DeprecationMessage_ is specified. +> **Important:** if the constructor used for the default value is non-const, then the generated constructor will use +> optional value to prevent compilation error. However, this implies certain limitation for default values of nullable +> types, which do not provide const constructor (i.a. Blob type or custom structures that are not annotated as `@Immutable`). +> +> If the field is nullable and its type does not provide const constructor then the only default value that is accepted +> can be null. If any other value is used then the generator will raise validation error. * **Attribute** **=** **"**_Annotation_**"**: marks an element to be marked with the given annotation in Dart generated code. _Annotation_ does not need to be prepended with `@`. _Annotation_ can contain parameters, e.g. `@Dart(Attribute="Deprecated(\"It's deprecated.\")")`. If some of the parameters are string literals, their enclosing diff --git a/functional-tests/functional/dart/test/Defaults_test.dart b/functional-tests/functional/dart/test/Defaults_test.dart index c206f4d830..ce8c802e24 100644 --- a/functional-tests/functional/dart/test/Defaults_test.dart +++ b/functional-tests/functional/dart/test/Defaults_test.dart @@ -18,6 +18,7 @@ // // ------------------------------------------------------------------------------------------------- +import 'dart:typed_data'; import "package:test/test.dart"; import "package:functional/test.dart"; import "../test_suite.dart"; @@ -103,6 +104,48 @@ void main() { expect(result.intField, 13); expect(result.stringField, "foobar"); }); + _testSuite.test("Check positional defaults for non-const constructible types", () { + // Case 1: all defaults. + final first = PosDefaultStructWithCustomStructsFields(); + expect(first.nonConstCtorField0.intField, 42); + expect(first.nonConstCtorField1.someField1.intField, 42); + expect(first.nonConstCtorField2.stringField, "Some string"); + expect(first.nonConstCtorField3.nullableListField, null); + expect(first.nonConstCtorField5, Uint8List.fromList([])); + expect(first.nonConstCtorField6, Uint8List.fromList([222, 173, 190, 239])); + expect(first.nonConstCtorField7, null); + + // Case 2: custom values. + final second = PosDefaultStructWithCustomStructsFields( + // Fields with const constructors. + AnotherImmutableStructWithDefaults.withDefaults(), + null, + [], + null, + 0, + 0.0, + null, + null, + // Fields without const constructor. + StructWithAllDefaults(21, "ABC"), + PosDefaultStructWithFieldUsingImmutableStruct(), + SomeMutableCustomStructWithDefaults(21, "Another string", [7, 7, 7]), + StructWithNullableCollectionDefaults(), + StructWithAllDefaults(44, "DEF"), + Uint8List.fromList([1, 2, 3]), + Uint8List.fromList([4, 5, 6]), + Uint8List.fromList([7, 8, 9]) + ); + + expect(second.nonConstCtorField0.intField, 21); + expect(second.nonConstCtorField1.someField1.intField, 42); + expect(second.nonConstCtorField2.stringField, "Another string"); + expect(second.nonConstCtorField3.nullableListField, null); + expect(second.nonConstCtorField4?.stringField, "DEF"); + expect(second.nonConstCtorField5, Uint8List.fromList([1, 2, 3])); + expect(second.nonConstCtorField6, Uint8List.fromList([4, 5, 6])); + expect(second.nonConstCtorField7, Uint8List.fromList([7, 8, 9])); + }); _testSuite.test("Check positional enumerator defaults", () { final result = StructWithEnums(); diff --git a/functional-tests/functional/input/lime/PositionalDefaults.lime b/functional-tests/functional/input/lime/PositionalDefaults.lime index bc6ad208a6..c9275cacff 100644 --- a/functional-tests/functional/input/lime/PositionalDefaults.lime +++ b/functional-tests/functional/input/lime/PositionalDefaults.lime @@ -72,6 +72,36 @@ struct PosDefaultStructWithFieldUsingImmutableStruct { someField1: AnotherImmutableStructWithDefaults = {} } +@Dart(PositionalDefaults) +@Java(Skip) @Swift(Skip) +struct SomeMutableCustomStructWithDefaults { + intField: Int = 77 + stringField: String = "Some string" + listField: List = [1, 2, 3] +} + +@Dart(PositionalDefaults) +@Java(Skip) @Swift(Skip) +struct PosDefaultStructWithCustomStructsFields { + constCtorField0: AnotherImmutableStructWithDefaults = {} + constCtorField1: AnotherImmutableStructWithDefaults? = {} + constCtorField2: List = ["abc", "def", "ghi"] + constCtorField3: Map? = null + constCtorField4: Int = 77 + constCtorField5: Double = 77.77 + constCtorField6: AnotherImmutableStructWithDefaults? = {} + constCtorField7: AnotherImmutableStructWithDefaults? = null + + nonConstCtorField0: StructWithAllDefaults = {} + nonConstCtorField1: PosDefaultStructWithFieldUsingImmutableStruct = {} + nonConstCtorField2: SomeMutableCustomStructWithDefaults = {} + nonConstCtorField3: StructWithNullableCollectionDefaults = {} + nonConstCtorField4: StructWithAllDefaults? = null + nonConstCtorField5: Blob = [] + nonConstCtorField6: Blob = [222, 173, 190, 239] + nonConstCtorField7: Blob? = null +} + @Dart(PositionalDefaults) @Java(Skip) @Swift(Skip) struct PosDefaultsWithDuration { diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidator.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidator.kt new file mode 100644 index 0000000000..7a8f3a1861 --- /dev/null +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidator.kt @@ -0,0 +1,76 @@ +/* + * Copyright (C) 2016-2024 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package com.here.gluecodium.generator.dart + +import com.here.gluecodium.common.LimeLogger +import com.here.gluecodium.model.lime.LimeAttributeType.DART +import com.here.gluecodium.model.lime.LimeAttributeType.IMMUTABLE +import com.here.gluecodium.model.lime.LimeAttributeValueType +import com.here.gluecodium.model.lime.LimeBasicType +import com.here.gluecodium.model.lime.LimeElement +import com.here.gluecodium.model.lime.LimeStruct +import com.here.gluecodium.model.lime.LimeValue + +class DartDefaultValuesValidator(private val logger: LimeLogger) { + fun validate(referenceMap: Map): Boolean { + val allStructs = referenceMap.values.filterIsInstance() + return !allStructs.map { validatePositionalDefaults(it) }.contains(false) + } + + private fun validatePositionalDefaults(limeStruct: LimeStruct): Boolean { + if (!limeStruct.attributes.have(DART, LimeAttributeValueType.POSITIONAL_DEFAULTS)) { + return true + } + + var result = true + for (field in limeStruct.initializedFields) { + // Types other than BLOB and custom LimeStructs provide const constructors. + val basicType = field.typeRef.type as? LimeBasicType + if (field.typeRef.type !is LimeStruct && (basicType?.typeId != LimeBasicType.TypeId.BLOB)) { + continue + } + + // Custom immutable types provide const constructors. + if (field.typeRef.type.attributes.have(IMMUTABLE)) { + continue + } + + // If the type does not provide const constructor, then generated 'PositionalDefaults' constructor + // will utilize optional field to avoid compilation errors. + // However, if the user uses 'nullable type' and wants to set value that is different from 'null' as the + // default one, then if somebody passed 'null' to the generated constructor, then it wouldn't be honored. + // + // Do not allow users to do that. + if (field.typeRef.isNullable && field.defaultValue!! !is LimeValue.Null) { + logger.error(field, "$NULLABLE_ERROR_REASON; please check $DOCS_LINK") + result = false + } + } + + return result + } + + companion object { + const val NULLABLE_ERROR_REASON = + "For '@PositionalDefaults' structs, nullable fields that do not provide" + + " const constructors must use 'null' default value" + const val DOCS_LINK = "https://github.com/heremaps/gluecodium/blob/master/docs/lime_attributes.md#dart-specific-attributes" + } +} diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGenerator.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGenerator.kt index a3784c254b..27e21d64f4 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGenerator.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGenerator.kt @@ -122,10 +122,12 @@ internal class DartGenerator : Generator { val dartNameResolver = DartNameResolver(ffiReferenceMap, nameRules, limeLogger, commentsProcessor) val ffiNameResolver = FfiNameResolver(ffiReferenceMap, nameRules, internalPrefix) - val validationResult = + val overloadsValidationResult = DartOverloadsValidator(dartNameResolver, limeLogger, overloadsWerror) .validate(dartFilteredModel.referenceMap.values) - if (!validationResult) { + val defaultValuesValidationResult = + DartDefaultValuesValidator(limeLogger).validate(dartFilteredModel.referenceMap) + if (!overloadsValidationResult || !defaultValuesValidationResult) { throw GluecodiumExecutionException("Validation errors found, see log for details.") } diff --git a/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGeneratorPredicates.kt b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGeneratorPredicates.kt index 704020cacb..30377deacb 100644 --- a/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGeneratorPredicates.kt +++ b/gluecodium/src/main/java/com/here/gluecodium/generator/dart/DartGeneratorPredicates.kt @@ -25,9 +25,11 @@ import com.here.gluecodium.generator.common.CommonGeneratorPredicates import com.here.gluecodium.model.lime.LimeAttributeType.DART import com.here.gluecodium.model.lime.LimeAttributeType.IMMUTABLE import com.here.gluecodium.model.lime.LimeAttributeValueType.POSITIONAL_DEFAULTS +import com.here.gluecodium.model.lime.LimeBasicType import com.here.gluecodium.model.lime.LimeContainer import com.here.gluecodium.model.lime.LimeElement import com.here.gluecodium.model.lime.LimeExternalDescriptor +import com.here.gluecodium.model.lime.LimeField import com.here.gluecodium.model.lime.LimeNamedElement import com.here.gluecodium.model.lime.LimeStruct import com.here.gluecodium.model.lime.LimeType @@ -45,6 +47,23 @@ internal class DartGeneratorPredicates( "allFieldsCtorIsPublic" to { limeStruct: Any -> limeStruct is LimeStruct && allFieldsCtorIsPublic(limeStruct) }, + "fieldHasConstCtor" to { limeField: Any -> + if (limeField is LimeField) { + when (limeField.typeRef.type) { + is LimeBasicType -> { + val basicType = limeField.typeRef.type as LimeBasicType + basicType.typeId != LimeBasicType.TypeId.BLOB + } + is LimeStruct -> limeField.typeRef.type.attributes.have(IMMUTABLE) + else -> true + } + } else { + false + } + }, + "fieldHasDefaultValue" to { limeField: Any -> + limeField is LimeField && limeField.defaultValue != null + }, "isInternal" to { element: Any -> when (element) { // Dart has no type nesting, so all types are "outside" and have to check for an internal outer type. diff --git a/gluecodium/src/main/resources/templates/dart/DartStructConstructors.mustache b/gluecodium/src/main/resources/templates/dart/DartStructConstructors.mustache index 13722d792f..e49c38917b 100644 --- a/gluecodium/src/main/resources/templates/dart/DartStructConstructors.mustache +++ b/gluecodium/src/main/resources/templates/dart/DartStructConstructors.mustache @@ -28,8 +28,27 @@ {{/instanceOf}}{{!! }} {{#if attributes.immutable}}const {{/if}}{{resolveName}}{{#if external.dart.converter}}Internal{{/if}}({{!! }}{{#uninitializedFields}}{{resolveName typeRef}} {{resolveName}}, {{/uninitializedFields}}{{!! - }}[{{#initializedFields}}{{resolveName typeRef}} {{resolveName}} = {{>constPrefix}}{{resolveName defaultValue}}{{#if iter.hasNext}}, {{/if}}{{/initializedFields}}]) - : {{#fields}}{{resolveName "visibility"}}{{resolveName}} = {{resolveName}}{{#if iter.hasNext}}, {{/if}}{{/fields}}; + }}[{{#initializedFields}}{{!! +}}{{#ifPredicate "fieldHasConstCtor"}}{{!! +}}{{resolveName typeRef}} {{resolveName}} = {{>constPrefix}}{{resolveName defaultValue}}{{#if iter.hasNext}}, {{/if}}{{!! +}}{{/ifPredicate}}{{!! +}}{{#unlessPredicate "fieldHasConstCtor"}}{{!! +}}{{resolveName typeRef.asNullable}} {{resolveName}} = null{{#if iter.hasNext}}, {{/if}}{{!! +}}{{/unlessPredicate}}{{!! +}}{{/initializedFields}}]) + : {{#fields}}{{!! +}}{{#ifPredicate "fieldHasDefaultValue"}}{{!! +}}{{#ifPredicate "fieldHasConstCtor"}}{{!! +}}{{resolveName "visibility"}}{{resolveName}} = {{resolveName}}{{#if iter.hasNext}}, {{/if}}{{!! +}}{{/ifPredicate}}{{!! +}}{{#unlessPredicate "fieldHasConstCtor"}}{{!! +}}{{resolveName "visibility"}}{{resolveName}} = {{resolveName}} ?? {{resolveName defaultValue}}{{#if iter.hasNext}}, {{/if}}{{!! +}}{{/unlessPredicate}}{{!! +}}{{/ifPredicate}}{{!! +}}{{#unlessPredicate "fieldHasDefaultValue"}}{{!! +}}{{resolveName "visibility"}}{{resolveName}} = {{resolveName}}{{#if iter.hasNext}}, {{/if}}{{!! +}}{{/unlessPredicate}}{{!! +}}{{/fields}}; {{/if}}{{!! }}{{#unless attributes.dart.positionalDefaults initializedFields}}{{!! diff --git a/gluecodium/src/test/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidatorTest.kt b/gluecodium/src/test/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidatorTest.kt new file mode 100644 index 0000000000..a7173eaaf2 --- /dev/null +++ b/gluecodium/src/test/java/com/here/gluecodium/generator/dart/DartDefaultValuesValidatorTest.kt @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2016-2024 HERE Europe B.V. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + * License-Filename: LICENSE + */ + +package com.here.gluecodium.generator.dart + +import com.here.gluecodium.model.lime.LimeAttributeType +import com.here.gluecodium.model.lime.LimeAttributeValueType +import com.here.gluecodium.model.lime.LimeAttributes +import com.here.gluecodium.model.lime.LimeBasicType +import com.here.gluecodium.model.lime.LimeBasicTypeRef +import com.here.gluecodium.model.lime.LimeDirectTypeRef +import com.here.gluecodium.model.lime.LimeElement +import com.here.gluecodium.model.lime.LimeField +import com.here.gluecodium.model.lime.LimePath +import com.here.gluecodium.model.lime.LimeStruct +import com.here.gluecodium.model.lime.LimeValue +import io.mockk.mockk +import org.junit.Assert.assertFalse +import org.junit.Assert.assertTrue +import org.junit.Test +import org.junit.runner.RunWith +import org.junit.runners.JUnit4 + +@RunWith(JUnit4::class) +class DartDefaultValuesValidatorTest { + private val allElements = mutableMapOf() + private val typePath = LimePath(emptyList(), listOf("someType")) + private val anotherTypePath = LimePath(emptyList(), listOf("anotherType")) + private val anotherType = LimeStruct(path = anotherTypePath) + private val positionalDefaultsAttributes = + LimeAttributes.Builder().addAttribute( + LimeAttributeType.DART, + LimeAttributeValueType.POSITIONAL_DEFAULTS, + ).build() + private val immutableAttributes = LimeAttributes.Builder().addAttribute(LimeAttributeType.IMMUTABLE).build() + private val anotherImmutableType = LimeStruct(path = anotherTypePath, attributes = immutableAttributes) + private val validator = DartDefaultValuesValidator(mockk(relaxed = true)) + + @Test + fun validateStructWithoutPositionalDefaults() { + // Given a structure that is not annotated with 'PositionalDefaults' + // and has nullable field with default value different from 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeDirectTypeRef(type = anotherType, isNullable = true), + defaultValue = LimeValue.StructInitializer(LimeDirectTypeRef(type = anotherType, isNullable = true), listOf()), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field)) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation succeeds. + assertTrue(result) + } + + @Test + fun validatePositionalDefaultsStructThatDoesNotHaveStructFields() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable field that is not struct and has value different from 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeBasicTypeRef(LimeBasicType.TypeId.INT32, isNullable = true), + defaultValue = LimeValue.Literal(LimeBasicTypeRef(LimeBasicType.TypeId.INT32, isNullable = true), "123"), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation succeeds. + assertTrue(result) + } + + @Test + fun validatePositionalDefaultsStructThatHasImmutableField() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable field annotated as immutable with default value different from 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeDirectTypeRef(type = anotherImmutableType, isNullable = true), + defaultValue = LimeValue.StructInitializer(LimeDirectTypeRef(type = anotherImmutableType, isNullable = true), listOf()), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation succeeds. + assertTrue(result) + } + + @Test + fun validatePositionalDefaultsStructThatHasMutableStructFieldEqualsNull() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable field with default value equals 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeDirectTypeRef(type = anotherType, isNullable = true), + defaultValue = LimeValue.Null(LimeDirectTypeRef(type = anotherType, isNullable = true)), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation succeeds. + assertTrue(result) + } + + @Test + fun validatePositionalDefaultsStructThatHasMutableStructFieldNotEqualNull() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable field with default value different from 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeDirectTypeRef(type = anotherType, isNullable = true), + defaultValue = LimeValue.StructInitializer(LimeDirectTypeRef(type = anotherType, isNullable = true), listOf()), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation fails. + assertFalse(result) + } + + @Test + fun validatePositionalDefaultsStructThatHasMutableBlobFieldEqualsNull() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable BLOB field with default value equals 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeBasicTypeRef(LimeBasicType.TypeId.BLOB, isNullable = true), + defaultValue = LimeValue.Null(LimeBasicTypeRef(LimeBasicType.TypeId.BLOB, isNullable = true)), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation succeeds. + assertTrue(result) + } + + @Test + fun validatePositionalDefaultsStructThatHasMutableBlobFieldNotEqualToNull() { + // Given a structure that is annotated with 'PositionalDefaults' + // and has nullable BLOB field with default value different from 'null'. + val field = + LimeField( + path = typePath.child("someField"), + typeRef = LimeBasicTypeRef(LimeBasicType.TypeId.BLOB, isNullable = true), + defaultValue = + LimeValue.InitializerList( + type = LimeBasicTypeRef(LimeBasicType.TypeId.BLOB, isNullable = true), + listOf( + LimeValue.Literal(type = LimeBasicTypeRef(LimeBasicType.TypeId.BLOB), value = "128"), + LimeValue.Literal(type = LimeBasicTypeRef(LimeBasicType.TypeId.BLOB), value = "230"), + ), + ), + ) + + val structWithFields = LimeStruct(path = typePath, fields = listOf(field), attributes = positionalDefaultsAttributes) + allElements[typePath.toString()] = structWithFields + + // When validating default values. + val result = validator.validate(allElements) + + // Then validation fails. + assertFalse(result) + } +} diff --git a/gluecodium/src/test/resources/smoke/defaults/input/DartPositionalDefaults.lime b/gluecodium/src/test/resources/smoke/defaults/input/DartPositionalDefaults.lime index df1d944637..43caa3eb51 100644 --- a/gluecodium/src/test/resources/smoke/defaults/input/DartPositionalDefaults.lime +++ b/gluecodium/src/test/resources/smoke/defaults/input/DartPositionalDefaults.lime @@ -62,6 +62,35 @@ struct PosDefaultStructWithFieldUsingImmutableStruct { someField1: ImmutableStructWithDefaults = {} } +@Java(Skip) @Swift(Skip) +struct SomeMutableCustomStructWithDefaults { + intField: Int = 77 + stringField: String = "Some string" + listField: List = [1, 2, 3] +} + +@Dart(PositionalDefaults) +@Java(Skip) @Swift(Skip) +struct PosDefaultStructWithCustomStructsFields { + constCtorField0: ImmutableStructWithDefaults = {} + constCtorField1: ImmutableStructWithDefaults? = {} + constCtorField2: List = ["abc", "def", "ghi"] + constCtorField3: Map? = null + constCtorField4: Int = 77 + constCtorField5: Double = 77.77 + constCtorField6: ImmutableStructWithDefaults? = {} + constCtorField7: ImmutableStructWithDefaults? = null + + nonConstCtorField0: StructWithAllDefaults = {} + nonConstCtorField1: PosDefaultStructWithFieldUsingImmutableStruct = {} + nonConstCtorField2: SomeMutableCustomStructWithDefaults = {} + nonConstCtorField3: StructWithNullableCollectionDefaults = {} + nonConstCtorField4: StructWithAllDefaults? = null + nonConstCtorField5: Blob = [] + nonConstCtorField6: Blob = [222, 173, 190, 239] + nonConstCtorField7: Blob? = null +} + // Foo Bar this is a comment // @constructor buzz fizz @Dart(PositionalDefaults = "Sorry, this is deprecated.") diff --git a/gluecodium/src/test/resources/smoke/defaults/output/dart/lib/src/smoke/pos_default_struct_with_custom_structs_fields.dart b/gluecodium/src/test/resources/smoke/defaults/output/dart/lib/src/smoke/pos_default_struct_with_custom_structs_fields.dart new file mode 100644 index 0000000000..fc056284f0 --- /dev/null +++ b/gluecodium/src/test/resources/smoke/defaults/output/dart/lib/src/smoke/pos_default_struct_with_custom_structs_fields.dart @@ -0,0 +1,261 @@ + + +import 'dart:ffi'; +import 'dart:typed_data'; +import 'package:library/src/_library_context.dart' as __lib; +import 'package:library/src/builtin_types__conversion.dart'; +import 'package:library/src/generic_types__conversion.dart'; +import 'package:library/src/smoke/immutable_struct_with_defaults.dart'; +import 'package:library/src/smoke/pos_default_struct_with_field_using_immutable_struct.dart'; +import 'package:library/src/smoke/some_mutable_custom_struct_with_defaults.dart'; +import 'package:library/src/smoke/struct_with_all_defaults.dart'; +import 'package:library/src/smoke/struct_with_nullable_collection_defaults.dart'; + + +class PosDefaultStructWithCustomStructsFields { + ImmutableStructWithDefaults constCtorField0; + + ImmutableStructWithDefaults? constCtorField1; + + List constCtorField2; + + Map? constCtorField3; + + int constCtorField4; + + double constCtorField5; + + ImmutableStructWithDefaults? constCtorField6; + + ImmutableStructWithDefaults? constCtorField7; + + StructWithAllDefaults nonConstCtorField0; + + PosDefaultStructWithFieldUsingImmutableStruct nonConstCtorField1; + + SomeMutableCustomStructWithDefaults nonConstCtorField2; + + StructWithNullableCollectionDefaults nonConstCtorField3; + + StructWithAllDefaults? nonConstCtorField4; + + Uint8List nonConstCtorField5; + + Uint8List nonConstCtorField6; + + Uint8List? nonConstCtorField7; + + PosDefaultStructWithCustomStructsFields([ImmutableStructWithDefaults constCtorField0 = const ImmutableStructWithDefaults.withDefaults(), ImmutableStructWithDefaults? constCtorField1 = const ImmutableStructWithDefaults.withDefaults(), List constCtorField2 = const ["abc", "def", "ghi"], Map? constCtorField3 = null, int constCtorField4 = 77, double constCtorField5 = 77.77, ImmutableStructWithDefaults? constCtorField6 = const ImmutableStructWithDefaults.withDefaults(), ImmutableStructWithDefaults? constCtorField7 = null, StructWithAllDefaults? nonConstCtorField0 = null, PosDefaultStructWithFieldUsingImmutableStruct? nonConstCtorField1 = null, SomeMutableCustomStructWithDefaults? nonConstCtorField2 = null, StructWithNullableCollectionDefaults? nonConstCtorField3 = null, StructWithAllDefaults? nonConstCtorField4 = null, Uint8List? nonConstCtorField5 = null, Uint8List? nonConstCtorField6 = null, Uint8List? nonConstCtorField7 = null]) + : constCtorField0 = constCtorField0, constCtorField1 = constCtorField1, constCtorField2 = constCtorField2, constCtorField3 = constCtorField3, constCtorField4 = constCtorField4, constCtorField5 = constCtorField5, constCtorField6 = constCtorField6, constCtorField7 = constCtorField7, nonConstCtorField0 = nonConstCtorField0 ?? StructWithAllDefaults(), nonConstCtorField1 = nonConstCtorField1 ?? PosDefaultStructWithFieldUsingImmutableStruct(), nonConstCtorField2 = nonConstCtorField2 ?? SomeMutableCustomStructWithDefaults(), nonConstCtorField3 = nonConstCtorField3 ?? StructWithNullableCollectionDefaults(), nonConstCtorField4 = nonConstCtorField4 ?? null, nonConstCtorField5 = nonConstCtorField5 ?? Uint8List.fromList([]), nonConstCtorField6 = nonConstCtorField6 ?? Uint8List.fromList([222, 173, 190, 239]), nonConstCtorField7 = nonConstCtorField7 ?? null; +} + + +// PosDefaultStructWithCustomStructsFields "private" section, not exported. + +final _smokePosdefaultstructwithcustomstructsfieldsCreateHandle = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer, Pointer, Pointer, Pointer, Int32, Double, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer), + Pointer Function(Pointer, Pointer, Pointer, Pointer, int, double, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer, Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_create_handle')); +final _smokePosdefaultstructwithcustomstructsfieldsReleaseHandle = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Void Function(Pointer), + void Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_release_handle')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField0 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField0')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField1 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField1')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField2 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField2')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField3 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField3')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField4 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Int32 Function(Pointer), + int Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField4')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField5 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Double Function(Pointer), + double Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField5')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField6 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField6')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField7 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_constCtorField7')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField0 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField0')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField1 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField1')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField2 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField2')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField3 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField3')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField4 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField4')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField5 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField5')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField6 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField6')); +final _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField7 = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_field_nonConstCtorField7')); + + + +Pointer smokePosdefaultstructwithcustomstructsfieldsToFfi(PosDefaultStructWithCustomStructsFields value) { + final _constCtorField0Handle = smokeImmutablestructwithdefaultsToFfi(value.constCtorField0); + final _constCtorField1Handle = smokeImmutablestructwithdefaultsToFfiNullable(value.constCtorField1); + final _constCtorField2Handle = foobarListofStringToFfi(value.constCtorField2); + final _constCtorField3Handle = foobarMapofStringToStringToFfiNullable(value.constCtorField3); + final _constCtorField4Handle = (value.constCtorField4); + final _constCtorField5Handle = (value.constCtorField5); + final _constCtorField6Handle = smokeImmutablestructwithdefaultsToFfiNullable(value.constCtorField6); + final _constCtorField7Handle = smokeImmutablestructwithdefaultsToFfiNullable(value.constCtorField7); + final _nonConstCtorField0Handle = smokeStructwithalldefaultsToFfi(value.nonConstCtorField0); + final _nonConstCtorField1Handle = smokePosdefaultstructwithfieldusingimmutablestructToFfi(value.nonConstCtorField1); + final _nonConstCtorField2Handle = smokeSomemutablecustomstructwithdefaultsToFfi(value.nonConstCtorField2); + final _nonConstCtorField3Handle = smokeStructwithnullablecollectiondefaultsToFfi(value.nonConstCtorField3); + final _nonConstCtorField4Handle = smokeStructwithalldefaultsToFfiNullable(value.nonConstCtorField4); + final _nonConstCtorField5Handle = blobToFfi(value.nonConstCtorField5); + final _nonConstCtorField6Handle = blobToFfi(value.nonConstCtorField6); + final _nonConstCtorField7Handle = blobToFfiNullable(value.nonConstCtorField7); + final _result = _smokePosdefaultstructwithcustomstructsfieldsCreateHandle(_constCtorField0Handle, _constCtorField1Handle, _constCtorField2Handle, _constCtorField3Handle, _constCtorField4Handle, _constCtorField5Handle, _constCtorField6Handle, _constCtorField7Handle, _nonConstCtorField0Handle, _nonConstCtorField1Handle, _nonConstCtorField2Handle, _nonConstCtorField3Handle, _nonConstCtorField4Handle, _nonConstCtorField5Handle, _nonConstCtorField6Handle, _nonConstCtorField7Handle); + smokeImmutablestructwithdefaultsReleaseFfiHandle(_constCtorField0Handle); + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField1Handle); + foobarListofStringReleaseFfiHandle(_constCtorField2Handle); + foobarMapofStringToStringReleaseFfiHandleNullable(_constCtorField3Handle); + + + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField6Handle); + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField7Handle); + smokeStructwithalldefaultsReleaseFfiHandle(_nonConstCtorField0Handle); + smokePosdefaultstructwithfieldusingimmutablestructReleaseFfiHandle(_nonConstCtorField1Handle); + smokeSomemutablecustomstructwithdefaultsReleaseFfiHandle(_nonConstCtorField2Handle); + smokeStructwithnullablecollectiondefaultsReleaseFfiHandle(_nonConstCtorField3Handle); + smokeStructwithalldefaultsReleaseFfiHandleNullable(_nonConstCtorField4Handle); + blobReleaseFfiHandle(_nonConstCtorField5Handle); + blobReleaseFfiHandle(_nonConstCtorField6Handle); + blobReleaseFfiHandleNullable(_nonConstCtorField7Handle); + return _result; +} + +PosDefaultStructWithCustomStructsFields smokePosdefaultstructwithcustomstructsfieldsFromFfi(Pointer handle) { + final _constCtorField0Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField0(handle); + final _constCtorField1Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField1(handle); + final _constCtorField2Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField2(handle); + final _constCtorField3Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField3(handle); + final _constCtorField4Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField4(handle); + final _constCtorField5Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField5(handle); + final _constCtorField6Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField6(handle); + final _constCtorField7Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldconstCtorField7(handle); + final _nonConstCtorField0Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField0(handle); + final _nonConstCtorField1Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField1(handle); + final _nonConstCtorField2Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField2(handle); + final _nonConstCtorField3Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField3(handle); + final _nonConstCtorField4Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField4(handle); + final _nonConstCtorField5Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField5(handle); + final _nonConstCtorField6Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField6(handle); + final _nonConstCtorField7Handle = _smokePosdefaultstructwithcustomstructsfieldsGetFieldnonConstCtorField7(handle); + try { + return PosDefaultStructWithCustomStructsFields( + smokeImmutablestructwithdefaultsFromFfi(_constCtorField0Handle), + smokeImmutablestructwithdefaultsFromFfiNullable(_constCtorField1Handle), + foobarListofStringFromFfi(_constCtorField2Handle), + foobarMapofStringToStringFromFfiNullable(_constCtorField3Handle), + (_constCtorField4Handle), + (_constCtorField5Handle), + smokeImmutablestructwithdefaultsFromFfiNullable(_constCtorField6Handle), + smokeImmutablestructwithdefaultsFromFfiNullable(_constCtorField7Handle), + smokeStructwithalldefaultsFromFfi(_nonConstCtorField0Handle), + smokePosdefaultstructwithfieldusingimmutablestructFromFfi(_nonConstCtorField1Handle), + smokeSomemutablecustomstructwithdefaultsFromFfi(_nonConstCtorField2Handle), + smokeStructwithnullablecollectiondefaultsFromFfi(_nonConstCtorField3Handle), + smokeStructwithalldefaultsFromFfiNullable(_nonConstCtorField4Handle), + blobFromFfi(_nonConstCtorField5Handle), + blobFromFfi(_nonConstCtorField6Handle), + blobFromFfiNullable(_nonConstCtorField7Handle) + ); + } finally { + smokeImmutablestructwithdefaultsReleaseFfiHandle(_constCtorField0Handle); + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField1Handle); + foobarListofStringReleaseFfiHandle(_constCtorField2Handle); + foobarMapofStringToStringReleaseFfiHandleNullable(_constCtorField3Handle); + + + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField6Handle); + smokeImmutablestructwithdefaultsReleaseFfiHandleNullable(_constCtorField7Handle); + smokeStructwithalldefaultsReleaseFfiHandle(_nonConstCtorField0Handle); + smokePosdefaultstructwithfieldusingimmutablestructReleaseFfiHandle(_nonConstCtorField1Handle); + smokeSomemutablecustomstructwithdefaultsReleaseFfiHandle(_nonConstCtorField2Handle); + smokeStructwithnullablecollectiondefaultsReleaseFfiHandle(_nonConstCtorField3Handle); + smokeStructwithalldefaultsReleaseFfiHandleNullable(_nonConstCtorField4Handle); + blobReleaseFfiHandle(_nonConstCtorField5Handle); + blobReleaseFfiHandle(_nonConstCtorField6Handle); + blobReleaseFfiHandleNullable(_nonConstCtorField7Handle); + } +} + +void smokePosdefaultstructwithcustomstructsfieldsReleaseFfiHandle(Pointer handle) => _smokePosdefaultstructwithcustomstructsfieldsReleaseHandle(handle); + +// Nullable PosDefaultStructWithCustomStructsFields + +final _smokePosdefaultstructwithcustomstructsfieldsCreateHandleNullable = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_create_handle_nullable')); +final _smokePosdefaultstructwithcustomstructsfieldsReleaseHandleNullable = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Void Function(Pointer), + void Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_release_handle_nullable')); +final _smokePosdefaultstructwithcustomstructsfieldsGetValueNullable = __lib.catchArgumentError(() => __lib.nativeLibrary.lookupFunction< + Pointer Function(Pointer), + Pointer Function(Pointer) + >('library_smoke_PosDefaultStructWithCustomStructsFields_get_value_nullable')); + +Pointer smokePosdefaultstructwithcustomstructsfieldsToFfiNullable(PosDefaultStructWithCustomStructsFields? value) { + if (value == null) return Pointer.fromAddress(0); + final _handle = smokePosdefaultstructwithcustomstructsfieldsToFfi(value); + final result = _smokePosdefaultstructwithcustomstructsfieldsCreateHandleNullable(_handle); + smokePosdefaultstructwithcustomstructsfieldsReleaseFfiHandle(_handle); + return result; +} + +PosDefaultStructWithCustomStructsFields? smokePosdefaultstructwithcustomstructsfieldsFromFfiNullable(Pointer handle) { + if (handle.address == 0) return null; + final _handle = _smokePosdefaultstructwithcustomstructsfieldsGetValueNullable(handle); + final result = smokePosdefaultstructwithcustomstructsfieldsFromFfi(_handle); + smokePosdefaultstructwithcustomstructsfieldsReleaseFfiHandle(_handle); + return result; +} + +void smokePosdefaultstructwithcustomstructsfieldsReleaseFfiHandleNullable(Pointer handle) => + _smokePosdefaultstructwithcustomstructsfieldsReleaseHandleNullable(handle); + +// End of PosDefaultStructWithCustomStructsFields "private" section. + +