1. Unique features of the Dart platform

Before we take a closer look at reliable air safety and FFI, let’s discuss how the Dart platform fits our goals. Programming languages tend to share many functions. For example, many languages support object-oriented programming or can run on the Web. What really sets languages apart is their unique combination of functionsDart’s unique capabilities cover three areas:

  • Portability: Efficient compilers generate x86 and ARM machine code for devices and optimized JavaScript for the Web. A broad target is support: mobile devices, desktops, application backends, etc. 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, iterative development of native devices and the Web. Dart provides rich constructs such as memory-isolated threads and asynchronous/wait for common concurrent and event-driven application patterns.

  • Robust: Dart’s robust, air-safe type system catches errors during development. The overall platform is highly scalable and reliable, with a wide range of applications that have been in production for over a decade, including business-critical applications such as Google Ads and Google Assistant.

Reliable null security makes the type system more powerful and enables better performance. Dart FFI lets you use existing C libraries for better portability and use highly tuned C code for performance-critical tasks.

2. Reliable air safety

Air safety is the biggest addition to the Dart language since Dart 2.0 introduced a reliable air safety type system. Null security further enhances the type system, enabling you to catch null errors, which are a common cause of application crashes. By choosing null security, you can prevent production crashes by catching null errors during development. Proper air safety is designed around a few core principles. Let’s revisit how these principles affect you as a developer.

Null by default: a fundamental change to the type system

The core challenge before null security was that you couldn’t tell the difference between code that was expected to pass null values and code that couldn’t use them. A few months ago, we found a bug in The MAIN Flutter channel that would crash various Flutter tool commands on certain machine configurations with a NULL error: The method ‘>=’ was called on NULL. The fundamental problem is code like this:

final intmajor = version? .major;final intminor = version? .minor;if (globals.platform.isMacOS) {
  // Plugin path of Android Studio changed after Version 4.1
  if (major >= 4 && minor >= 1) {...Copy the code

Can you spot the error? becauseversionCan I do fornull, somajorandminorWill be able tonull. It may seem easy to spot this error in isolation, but in reality, even after a rigorous code review process (as used in the main Flutter branch), this code can occur all the time. For security reasons, static analysis catches this problem immediately. (You can try it out in DartPad.)That was a very simple mistake. During the early use of NULL security in our code internally at Google, we found a number of complex errors. Some of these are bugs that have existed for years, but without additional static checks for NULL security, the team could not find the cause. Here are some examples:

  • An internal team found that they often checked for nevernullOf the expression ofnullValue. useprotobufThis problem occurs most often in code in which 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.
  • Google PayGroup inFlutterSome errors have been found in the code that attempt to access State out of contextFlutterObject failedWidget. In the implementationnullBefore it is safe, these objects will be returnednullAnd cover up mistakes; For security reasons, reliable profilers determine that these properties are never empty and raise parsing errors.
  • FlutterThe team found an error if the error will benullPass tosceneParameters,FlutterThe engine could crashWindow.render(). In thenullDuring the secure migration, they added a hint that willSceneMarked asnon-nullableYou can then easily prevent potential application crashes that could trigger NULL.

4. Use non-null by default

Once null security is enabled, variable declarations are basically changed because the default type is not 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 a type to make it explicit:

// aNullableInt can hold either an integer or null.
int? aNullableInt = null;
Copy the code

The null-security implementation is robust, with rich static flow analysis capabilities that make it easier to use 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 keywordrequired. When the named parameter is marked asrequiredAnalysis errors occur when the caller forgets to provide parameters (which often happens in the Flutter widget API) :

Step 5 Migrate to invalid security

Since reliable security is a fundamental change to our typing system, it would be extremely disruptive if we insisted on mandatory adoption. In this way, you are right to decide that air security is an optional feature: you can use Dart2.12 without being forced to enable air security. You can even rely on packages that already have empty security enabled, regardless of whether your application or package has it enabled.

To help you migrate existing code tonull safetyWe provide migration tools and migration guidance. The tool starts by analyzing all existing code. You can then interactively view the nullability properties inferred by the tool. If you disagree with any of the tool’s conclusions, you can add nullability hints to change the inference. Adding some migration hints can have a huge impact on migration quality.Currently, usingdart createandflutter createNew packages and applications created without enabling reliable empty security. When we see that most ecosystems have migrated, we want to change this in a future stable release. You can easily use it in newly created packages or applicationsnull safety, you can use the commanddart migrate.

6. Zero-security migration status of the Dart ecosystem

Over the past year, we have offered several preview and Beta versions of sound ineffective security, with the goal of embedding software packages that support ineffective security into the ecosystem. This preparation is important because we recommend migrating sequentially to ensure sound security – you should not migrate a package or application before all of its dependencies have been migrated. We have released null secure versions of hundreds of software packages provided by Dart, Flutter, Firebase, and Material teams. Also, we have received tremendous support from the amazing Dart and Flutter ecosystems, so pub.dev now has over a thousand packages that support NULL security. Importantly, the most popular packages were migrated first, so 98% of the top 100 most popular packages, 78% of the top 250 packages, and 57% of the top 500 packages have zero security support in time for today’s release. We expect to see more null-safe packages on pub.dev in the coming weeks. Our analysis shows that the vast majority of packages on pub.dev have been unblocked and can begin migration.

7. The benefits of completely reliable security

Once fully migrated, Dart’s NULL Safety is ready. This means Dart 100% ensures that expressions with non-NULL types cannot be null. When Dart analyzes your code and determines that a variable cannot be nullable, that variable is always nullable. Dart shares solid security with Swift, but few other programming languages. Dart’s null Safety robustness has another welcome implication: it means your program can be smaller and faster. Dart can be optimized because it ensures that non-null variables are never null. For example, the Dart Ahead of Time (AOT) compiler can generate smaller, faster native code because it doesn’t need to add checks for NULL when it knows the variable isn’t NULL.

Dart FFI, which integrates Dart with the C library

Dart FFI enables you to leverage existing code in the C library for better portability and integrate it with highly tuned C code to perform performance-critical tasks. As of Dart 2.12, THE Dart FFI has been taken out of Beta and is now considered stable and ready for production. We also added some new features, including nested structures and pass-by-value structures.

9. Pass structure by value

Structures can be passed by reference and by value in C code. FFI previously only supported passing by reference, but starting with Dart 2.12, you can pass structures by value. Here are two small examples of C functions passed by reference and value:

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

10. Nested structures

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.

11. The API changes

In order to declare FFI stable and support the above features, we made some minor API changes. Creating empty structures is now disallowed (break change # 44622) and a deprecation warning is generated. You can use the new type Opaque to represent empty structures. The DART: FFI functions sizeOf, elementAt, and ref now require compile-time type parameters (significant change # 44621). Because package: FFI has added new convenience features, there is no need to allocate and free additional boilerplate 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

12. Generate FFI binding automatically

For large API surfaces, writing Dart bindings that integrate with C code can be time-consuming. To ease this burden, we built a binding generator to automatically create FFI wrappers from C header files. We invite you to try: Package :ffigen.

13. FFI roadmap

With the core FFI platform complete, we shifted our focus to extending the FFI feature set with functionality layered on top of the core platform. Some of the features we are investigating include:

  • Abi-specific data types, for exampleint.long.size_t(# 36140)
  • Arrays in inline structures (# 35763)
  • Packaged structure (# 38158)
  • Union type (# 38491)
  • Expose finalizer to Dart (# 35770; But note that you can already use C’s finalizer.)

14. Example usage of FFI

We’ve seen many creative uses of Dart FFI to integrate with various C-based apis. Here are some examples:

  • open_fileIs a single file for opening across multiple platformsAPI. It uses FFI callsWindows.macOSandLinuxNative operating system API on.
  • win32Encapsulates the most commonWin32 APISo that you can call various directly from DartWindows API.
  • Objectbox is a fast database supported by a C-based implementation.
  • tflite_flutterUse FFI packagingTensorFlow Lite API.

15. What’s next for the Dart language?

Sound void security is the biggest change we’ve made to the Dart language in years. Next, we will consider making more incremental changes to the language and platform, building on our strong foundation. A quick look at some of the things we’re trying to do in the language design channel:

Type aliases (# 65) : You can create type aliases for non-functional types. For example, you can create a typedef and use it as a variable type:

typedef IntList = List<int>;
IntList il = [1.2.3];
Copy the code

Triple shift operator (# 120) : A new, fully rewritable >>> operator has been added for unsigned shifts on integers. Generic metadata annotations (# 1297) : Extend metadata annotations to also support annotations containing type parameters.

Static metaprogramming (# 1482) : Support for static metaprogramming – Dart programs generate new Dart source code at compile time, similar to Rust macros and Swift function generators. This feature is still in the early stages of exploration, but we think it will enable today’s use cases that rely on code generation.

16.Dart 2.12 is now available

Dutter 2.12 and the Flutter 2.0 SDK now provide FFI with reliable NULL safety and stability. Please take a moment to review known NULL safety issues with Dart and Flutter. If you find any additional issues, report them in the Dart Problem Tracker.

If you have developed packages published on pub.dev, check out the migration guide immediately and learn how to migrate for security. Migrating your package may help unblock other packages and applications that depend on it. We would also like to thank those who have moved!

We would like to hear about your experience with reliable security and FFI. Leave a comment below or tweet us @dart_lang.

Original address: medium.com/dartlang/an…