Dart 2.12 was released today with a stable air safety statement and Dart FFI release.
Empty security is the latest key productivity feature designed to help developers avoid empty errors, which are often difficult to detect.
FFI is an interoperability mechanism that lets developers call existing code written in C, such as the Windows Win32 API.
Unique features of the Dart platform
Before explaining the air safety statement and FFI in detail, let’s discuss how the Dart platform fits into our target platform.
Programming languages generally tend to share many features, for example many languages support object-oriented programming or run on the Web, but what really sets languages apart is their unique combination of features.
Dart’s unique capabilities cover three areas:
-
Portability: Efficient compilers generate x86 and ARM machine code for devices and optimized JavaScript code for the Web. So THE ongoing purpose of Dart is to support: mobile devices, desktops, application backends, and so on. A large number of libraries and software packages provide a consistent API that can be used on all platforms, further reducing the cost of creating true multi-platform applications.
-
Efficient: The Dart platform supports hot reload for rapid iteration and development on native devices and the Web. Dart provides rich constructs, such as Computers and Async /await, for dealing with common concurrent and event-driven patterns.
-
Robust: Dart is robust, and the NULL security system catches errors during development. Highly scalable and reliable across the platform, Dart has been used for more than a decade in production development, including business-critical applications such as Google Ads and Google Assistant.
PS: In fact, it has been widely used in recent years because of Flutter
A solid null-safety declaration makes the type system more powerful and delivers better performance, while Dart FFI allows developers to be more portable with existing C libraries and schedule performance-demanding tasks through C code.
Air safety statement
The null safety statement is the largest addition to the Dart language since it was introduced in Dart 2.0. Null security further enhances the type system, enabling developers to catch null errors at development time, which have been a common cause of application crashes in the past.
Sound air safety statements are designed around some core principles, and let’s take a look at the declaratory implications for developers.
Not null by default
The core challenge prior to null safety declarations was that developers could not tell the difference between code that passed null values and code that could not use null values.
A few months ago, we found a bug in the Flutter Master channel that would crash various Flutter tool commands on certain machine configurations and cause null errors: The method ‘>=’ was called on null, and The underlying problem is this code:
final int major = version?.major;
final int minor = version?.minor;
if (globals.platform.isMacOS) {
// plugin path of Android Studio changed after version 4.1.
if (major >= 4 && minor >= 1) {
...
Copy the code
Did you find anything wrong? Since version can be null, major and minor can also be null.
Such independent errors may seem easy to catch, but in reality such Code is always everywhere, even after a rigorous Code Review process such as Flutter Code Review. So for security reasons, static analysis catches this problem immediately.
That was a very simple error, and internally at Google, during the early use of NULL security, we found many complex errors, some of which were years old, but the team could not find the cause without additional static checks for NULL security.
Here are some examples:
-
One internal team found that they often checked for null values in expressions that could never be null. This problem occurs most often in code that uses Protobuf, where optional fields return default values when not set and are never null. In this way, the code incorrectly checks the default condition by confusing the default value with a null value.
-
The Google Pay team found some errors in the Flutter code that attempted to access the Widget of a Flutter object out of context in the State. Until NULl-safe is implemented, these objects return NULL and mask errors; For security reasons, declarative analysis determines that these attributes are never empty and raises an analysis error.
-
The Flutter team found an error that the Flutter engine might crash if the null scene argument was passed to window.render (). During null-safe migration, they added a hint to mark the Scene as non-nullable, and were then able to easily prevent potential application crashes that could trigger NULL.
Non-null is used by default
Once null security is enabled, the basic behavior of variable declarations is changed because the default type is non-null:
// In null-safe Dart, none of these can ever be null.
var i = 42; // Inferred to be an int.
String name = getFileName();
final b = Foo();
Copy the code
If you want to create a variable that can contain values or null, you need to pass? Add a suffix to the type declaration to make the variable appear explicitly in the variable declaration:
// aNullableInt can hold either an integer or null.
int? aNullableInt = null;
Copy the code
Null-security implementations are robust and have rich static flow analysis capabilities, making it easier to work with nullable types. For example, Dart raises the type of a local variable from nullable to non-Nullable after checking for NULL:
int definitelyInt(int? aNullableInt) {
if (aNullableInt == null) {
return 0;
}
// aNullableInt has now promoted to a non-null int.
return aNullableInt;
}
Copy the code
We also added a new keyword required, which causes an analysis error when the named parameter is marked required (which often happens in the Flutter widget API) and the caller forgets to provide the parameter:
Move gradually to null security
Because null security changes our coding habits, it would be extremely disruptive to insist on enforcing it. So we decided to let developers enable it when they need it most, so null security is an optional feature: You can use Dart 2.12 without being forced to, and you can even rely on packages that already have null security enabled, regardless of whether an application or package has null security enabled.
To help developers migrate existing code to a secure state, we provide migration tools and guidance. These tools first analyze all existing code, and then the developer can interactively view the nullability properties inferred by the tool.
If the developer disagrees with the tool’s conclusions, the inference can be changed by adding nullability hints, which can have a significant impact on the quality of the migration.
New packages and applications currently created using DART Create and Flutter Create do not enable null safety declarations. Dart Migrate is a simple way to enable air safety features.
Air safety migration status of the Dart ecosystem
Over the past year, we have provided preview and Beta versions of several air-safety statements with the goal of embedding air-safety software packages into the ecosystem.
This preparation is important because we recommend sequential migration to ensure that empty security claims do not affect the developer’s existing applications, and it is best not to enable the empty security configuration until all dependencies have been migrated.
Dart, Flutter, Firebase, and Material teams have released versions of hundreds of null-safe software packages, and we have received tremendous support from the amazing Dart and Flutter ecosystems, As a result pub.dev now has over a thousand packages that support NULL security.
Importantly, the most popular packages were migrated first, so as of today’s release, 98% of the top 100 most popular packages support NULL Safety, and 78% of the top 250 and 57% of the top 500 packages already support zero security.
We expect to see more null-safe packages on pub.dev in the coming weeks. Analysis shows that the vast majority of packages on pub.dev have been unblocked and can begin migration.
Benefits of complete null security
After full migration, Dart’s null-safety is enabled, which means Dart ensures 100% that expressions with non-NULL types cannot be null.
When Dart analyzes the developer’s code and determines that a variable cannot be null, it will always be null, whereas Dart shares a null-safe declaration with Swift, which is rare in other programming languages.
PS: Kotlin has something to say
The robustness of the Dart null safety claim has another implication: it means your program can be smaller and faster.
Dart can be optimized because it ensures that non-null variables are never null. The Dart Ahead of time (AOT) compiler, for example, can generate smaller, faster native code because it doesn’t need to add a null check when a variable is known not to be null.
Dart FFI, which integrates Dart with C
Dart FFI enables developers to leverage existing code in C for better portability and adapt C code integration to perform performance-demanding tasks.
As of Dart 2.12, Dart FFI has been taken out of Beta and is now considered stable and ready for production, and we have added a number of new features, including nested structures and pass-by-value structures.
Pass structure by value
You can pass structures by reference and by value in C code. FFI previously only supported passing structures by reference, but starting with Dart 2.12 developers can pass structures by value, for example:
struct Link {
double value;
Link* next;
};
void MoveByReference(Link* link) {
link->value = link->value + 10.0;
}
Coord MoveByValue(Link link) {
link.value = link.value + 10.0;
return link;
}
Copy the code
Nested structure
C apis typically use nested structures – structures that contain structures themselves, such as the following example:
struct Wheel {
int spokes;
};
struct Bike {
struct Wheel front;
struct Wheel rear;
int buildYear;
};
Copy the code
Starting with Dart 2.12, FFI supports nested structures.
API changes
In order to improve FFI stability and support the above features, we made some minor API changes.
Creating empty structures is now forbidden (#44622) and a deprecation warning is issued. Developers can use the new type Opaque to represent empty structures.
SizeOf, elementAt, and ref for DART :ffi now require compile-time type parameters (# 44621) because of the new convenience added to Package: FFi, there is no need to allocate and free memory in common cases.
// Allocate a pointer to an Utf8 array, fill it from a Dart string,
// pass it to a C function, convert the result, and free the arg.
//
// Before API change:
final pointer = allocate<Int8>(count: 10);
free(pointer);
final arg = Utf8.toUtf8('Michael');
var result = helloWorldInC(arg);
print(Utf8.fromUtf8(result);
free(arg);
// After API change:
final pointer = calloc<Int8>(10);
calloc.free(pointer);
final arg = 'Michael'.toNativeUtf8();
var result = helloWorldInC(arg);
print(result.toDartString);
calloc.free(arg);
Copy the code
Automatically generate FFI bindings
For large API exposures, writing Dart bindings that integrate with C code can be time-consuming. To alleviate this burden, we built a binding generator that automatically creates an FFI wrapper package: FFigen from C header files.
FFI roadmap
With the completion of the core FFI platform, we have shifted our focus to extending FFI functionality with layering capabilities on top of the core platform. Some of the features we are investigating include:
- Abi-specific data types, such as int, long, size_t # 36140
- Array # 35763 in an inline structure
- Packaged structure # 38158
- Union type # 38491
- Expose finalizers to Dart# 35770
Example usage of FFI
We’ve talked about many creative uses of Dart FFI to integrate with various C-based apis. Here are some examples:
-
Open_file is a single API for opening files across multiple platforms, using FFI to call native operating system apis on Windows, macOS, and Linux. Pub. Dev/packages/op…
-
Win32 encapsulates the most common Win32 apis, making it possible to call various Windows apis directly from Dart. Pub. Dev/packages/wi…
-
Objectbox is a fast database supported by a C-based implementation. Pub. Dev/packages/ob…
-
Tflite_flutter uses FFI to wrap the TensorFlow Lite API.
What’s next for the Dart language?
The Air Safety statement is the biggest change we’ve made to the Dart language in several years, and we’ll look to make more incremental changes to the language and platform to build on our strong foundation.
Some of the things we’re trying to do in the language design channel:
- Type aliases # 65: Type aliases can be created for non-functional types, such as one
typedef
And use it as a variable type:
typedef IntList = List <int>; IntList il = [1, 2, 3];Copy the code
-
Type Aliases # 120: Added a new, fully rewritable >>> operator for unsigned shifts on integers.
-
Generic Metadata Annotations # 1297: Extend metadata annotations to also support annotations containing type parameters.
-
Static Meta-programming # 1482: Support for static metaprogramming – Dart programs generate new Dart source code during compilation, similar to Rust macros and Swift function generators (this feature is still in the early stages of exploration, but we think it can enable use cases that rely on code generation today).
Dart 2.12 is available now
Dart 2.12 and the Flutter 2.0 SDK now offer reliable air safety and stable FFI, so please take a moment to review known invalid safety issues with Dart and Flutter. If you find any other issues, Please report these issues in the Dart Tracker.
Github.com/dart-lang/s…
If you have already published the package on pub.dev, check out the migration guide immediately and learn how to migrate for security. Migrating a package may help unblock other packages and applications that depend on it, and we have to thank those who have migrated!