Skip to content

Bridge classes

Ethan edited this page Feb 12, 2022 · 2 revisions

A bridge class allows one to extend, implement, or mixin a Dart class inside of the Eval environment. It also allows for instantiation of the original, unmodified class.

Bridge classes are used, for example, when building Flutter widgets. In Eval you might write:

class MyWidget extends StatelessWidget {
  MyWidget({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Container(child: Text("MyWidget"));
  }
}

which would make use of the following bridge class (abridged for clarity):

class $StatelessWidget$bridge extends StatelessWidget with $Bridge {
  
  const $StatelessWidget$bridge({Key? key}): super(key: key);

  @override
  EvalValue? $bridgeGet(String identifier) {
    switch (identifier) {
      case 'build':
        return EvalFunctionImpl((rt, target, args) => $Widget.wrap(super.build(args[0].$value)));
      default:
        throw UnimplementedError();
    }
  }
  
  @override
  Widget build(BuildContext context) => $_invoke('build', [$BuildContext.wrap(context)]);
}

In order to create an instance of MyWidget, the Eval environment will use the following steps:

  1. Instantiate a bridge super shim, which allows a bridge subclass to access superclass fields with super;
  2. Instantiate the MyWidget subclass (which contains an override of only the build function) using the super shim from step 1 as its superclass;
  3. Instantiate a bridge data object (which holds data about a specific bridge instance) using the active runtime, the MyWidget subclass as its subclass, and the MyWidget runtime type ID;
  4. Instantiate the $StatelessWidget$bridge bridge via its constructor from the bridge static function map;
  5. Assign the bridge data instance to the newly created bridge class via the global bridgeData Expando;
  6. Set the super shim's bridge class to the newly created bridge class;
  7. Return the $StatelessWidget$bridge bridge class.

This resulting instance is able to serve dual roles. Inside the Eval environment, the $bridgeGet method will be called to lookup fields and methods using a String. However if the bridge class is returned from the Eval environment, it will fully conform to the basic StatelessWidget type; and if the build method is called, since we've overridden it with $invoke, Eval will be able to redirect the invocation into a bridge call so that our custom build code is called.

Additionally, because all data about the subclass is contained in the bridge data instance, the resulting instance is transferrable. That is, it can be cast at runtime to another type if needed by simply assigning the bridge data to another bridge class; this is important since Eval is unable to produce bridge classes for every possible combination of interfaces and mixins.

Clone this wiki locally