Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[native_assets_cli] Syntax validation #2115

Merged
merged 2 commits into from
Mar 20, 2025
Merged

[native_assets_cli] Syntax validation #2115

merged 2 commits into from
Mar 20, 2025

Conversation

dcharkes
Copy link
Collaborator

@dcharkes dcharkes commented Mar 20, 2025

Bug: #1826

This PR adds generated validate functions on the syntax classes that check the type and nullability of the required fields.

This PR does not separate syntax and semantic validation in the ProtocolExtension as proposed in #2088. The inputs/outputs classes expose the underlying JSON and are lazy. So, they don't fail on syntax errors unless traversed.

This PR does not yet address:

To prevent crashes in the semantic validation, the semantic validation is not run when when syntactic errors are found. To prevent confusion for users, an extra error message is added that semantic validation is not run if syntactic errors are found.

We should consider also skipping extension validation if any errors occur in the base protocol, the validation code of extensions should be able to rely on that base protocol doesn't contain any errors.

Copy link

PR Health

Breaking changes ✔️
Package Change Current Version New Version Needed Version Looking good?
Changelog Entry ✔️
Package Changed Files

Changes to files need to be accounted for in their respective changelogs.

API leaks ✔️

The following packages contain symbols visible in the public API, but not exported by the library. Export these symbols or remove them from your publicly visible API.

Package Leaked API symbols
License Headers ✔️
// Copyright (c) 2025, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
Files
no missing headers

All source files should start with a license header.

Unrelated files missing license headers
Files
pkgs/jni/lib/src/third_party/generated_bindings.dart
pkgs/objective_c/lib/src/ns_input_stream.dart

List<Object?> list,
String key,
) {
var index = 0;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: I'd either use a normal C-style for loop and do final value = list[i] or use for (final (index, value) in list.indexed)

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How did I not know about .indexed!

throwFormatException(value, T, [key]);
}

List<String> validate<T extends Object?>(String key) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: T extends Object? is the same as just T but it's fine if you want to be more descriptive.

Not as important in generated code but a name like validateIsA better describes the type checking here.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah Object? == dynamic I thought it excludes dynamic, but it doesn't.

validate

I wanted to keep it analogous to get. We could do getA, but I feel it makes it less readable.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think T extends Object? is different to T regarding the avoid_dynamic_calls lint. I might be wrong on this though.

throwFormatException(value, T, [key]);
}

List<String> validate<T extends Object?>(String key) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does it make sense to have a full Dart class in a string? This feels like it should live somewhere the analyzer can look at it...

Copy link
Collaborator Author

@dcharkes dcharkes Mar 20, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I wanted to do that initially, but that means that invoking the code generator as a library needs to know where the code generator is so that it can find the file and read it as a String.

If we were to change the code generator to have a bin/ script we would know from the entry point. But we invoke it with a Dart API (and pass in a bunch of objects).

We could change the API to have to pass in URI of the package, but that also seems messy.

I'm open to suggestions.

(Orthogonal to this PR, so we should probably do that in a separate PR.)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Isn't the entry point in hook/tool/generate_syntax.dart, where you could hardcode the path to the file?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think passing the path to the file is not messy, but your choice.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes the Dart API have an extra param and less encapsulated. More ugly in the implementation but less ugly on call sites.

If this package ever gets published on pub, then the user would have to know the path of the package in the pub-cache and pass it in. That feels too messy.

(And publishing a full blown helper package package:json_syntax_runtime_lib like we have with package:jni, seems way too much overkill here.)

Let's keep the ugly inside instead of on the API.

@@ -28,6 +28,11 @@ class AndroidCodeConfig {
json.setOrRemove('target_ndk_api', value);
}

List<String> _validateTargetNdkApi() =>
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would add a header to the file detailing

  • where it was generated/what generates it
  • how to regenerate this, and when

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Orthogonal to this PR, let me do that in a separate PR

@@ -76,6 +91,26 @@ void _validateDataAsset(
errors.addAll(_validateFile('Data asset ${dataAsset.name} file', file));
}

List<String> _validateDataAssetSyntax(EncodedAsset encodedAsset) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Documentation would be much appreciated ;) List<String> is not a very expressive return type.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ah, we actually have typedef ValidationErrors. Good catch!

@dcharkes
Copy link
Collaborator Author

Thanks @HosseinYousefi & @mosuem !

@dcharkes dcharkes merged commit 4e534d3 into main Mar 20, 2025
32 checks passed
@dcharkes dcharkes deleted the syntax-validation branch March 20, 2025 13:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants