Skip to content

Commit

Permalink
Apply review comments from Yegor (flutter#474)
Browse files Browse the repository at this point in the history
  • Loading branch information
Hixie authored Oct 1, 2021
1 parent 1b7b7cc commit 9d1e048
Show file tree
Hide file tree
Showing 16 changed files with 203 additions and 134 deletions.
9 changes: 9 additions & 0 deletions customer_testing.sh
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,15 @@ flutter test
popd

pushd packages/rfw

# Update the examples packages so that the analysis doesn't get confused.
pushd example/remote
flutter packages get
popd
pushd example/wasm
flutter packages get
popd

flutter analyze --no-fatal-infos
if [[ "$OSTYPE" == "linux-gnu" ]]; then
# We only run the full tests on Linux because golden files differ
Expand Down
5 changes: 4 additions & 1 deletion packages/rfw/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
## NEXT
## 1.0.1

* Improve documentation.
* Provide constants for the file signatures.
* Minor efficiency improvements.
* Fix `unnecessary_import` lint errors.

## 1.0.0
Expand Down
13 changes: 13 additions & 0 deletions packages/rfw/example/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
There are several examples.

* `hello` shows how `RemoteWidget` can render some widgets from a
description interpreted at runtime.

* `local` shows how to create new local widget definitions for use in
remote widget descriptions.

* `remote` is a proof-of-concept showing files being obtained from a
remote server and rendered by the application at runtime.

* `wasm` shows how package:rfw can be combined with package:wasm to
configure logic as well as the user interface at runtime.
6 changes: 3 additions & 3 deletions packages/rfw/lib/formats.dart
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,6 @@
/// For client-side code, import `package:rfw/rfw.dart` instead.
library formats;

export 'dart/binary.dart';
export 'dart/model.dart';
export 'dart/text.dart';
export 'src/dart/binary.dart';
export 'src/dart/model.dart';
export 'src/dart/text.dart';
24 changes: 12 additions & 12 deletions packages/rfw/lib/rfw.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
/// (e.g. [https://pub.dev/packages/hetu_script](hetu_script)) to run
/// remotely-fetched logic locally:
///
/// ![The Remote Flutter Widgets once again come from the server and follow the same path via the network to the Runtime. The Runtime combines this with the Data model to generate the Flutter Widgets, which send state updates directly back to the runtime and user input to the Hard-coded client logic. That logic updates the Client data which updates the Data model, but also sends messages to the Scripting engine which is also on the Client. The Scripting engine is configured from Scripts obtained over the network, and generates Script data that also populates the Data model.](https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/images/overview2.png)
/// ![The Remote Flutter Widgets once again come from the server and follow the same path via the network to the Runtime. The Runtime combines this with the Data model to generate the Flutter Widgets, which send state updates directly back to the Runtime and user input to the Hard-coded client logic. That logic updates the Client data which updates the Data model, but also sends messages to the Scripting engine which is also on the Client. The Scripting engine is configured from Scripts obtained over the network, and generates Script data that also populates the Data model.](https://raw.githubusercontent.com/flutter/packages/master/packages/rfw/images/overview2.png)
///
///
/// ## Using the [RemoteWidget] widget
Expand All @@ -36,23 +36,23 @@
/// The methods for parsing the text format are not exported by
/// `package:rfw/rfw.dart` to discourage their use in client-side code.
///
/// ## Server-side dart
/// ## Server-side Dart
///
/// This package can be used in non-Flutter environments by importing
/// `package:rfw/formats.dart` rather than `package:rfw/rfw.dart`. In the
/// `formats` mode, the [Runtime] and [DynamicContent] objects, as well as the
/// [formats] mode, the [Runtime] and [DynamicContent] objects, as well as the
/// [RemoteWidget] widget, are not available, but the [parseDataFile] and
/// [parseLibraryFile] methods are. They can be used in conjunction with
/// [encodeDataBlob] and [encodeLibraryBlob] (respectively) to generate the
/// binary files used by client-side code.
library rfw;

export 'dart/binary.dart';
export 'dart/model.dart';
export 'dart/text.dart' hide parseDataFile, parseLibraryFile;
export 'flutter/argument_decoders.dart';
export 'flutter/content.dart';
export 'flutter/core_widgets.dart';
export 'flutter/material_widgets.dart';
export 'flutter/remote_widget.dart';
export 'flutter/runtime.dart';
export 'src/dart/binary.dart';
export 'src/dart/model.dart';
export 'src/dart/text.dart' hide parseDataFile, parseLibraryFile;
export 'src/flutter/argument_decoders.dart';
export 'src/flutter/content.dart';
export 'src/flutter/core_widgets.dart';
export 'src/flutter/material_widgets.dart';
export 'src/flutter/remote_widget.dart';
export 'src/flutter/runtime.dart';
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,26 @@ import 'dart:typed_data';

import 'model.dart';

/// The first four bytes of a Remote Flutter Widgets binary data blob.
///
/// This signature is automatically added by [encodeDataBlob] and is checked in
/// [decodeDataBlob].
///
/// See also:
///
/// * [libraryBlobSignature], which is the signature for binary library blobs.
const List<int> dataBlobSignature = <int>[0xFE, 0x52, 0x57, 0x44];

/// The first four bytes of a Remote Flutter Widgets binary library blob.
///
/// This signature is automatically added by [encodeLibraryBlob] and is checked
/// in [decodeLibraryBlob].
///
/// See also:
///
/// * [dataBlobSignature], which is the signature for binary data blobs.
const List<int> libraryBlobSignature = <int>[0xFE, 0x52, 0x46, 0x57];

/// Encode data as a Remote Flutter Widgets binary data blob.
///
/// See also:
Expand All @@ -20,7 +40,7 @@ import 'model.dart';
/// Remote Flutter Widgets binary library blobs.
Uint8List encodeDataBlob(Object value) {
final _BlobEncoder encoder = _BlobEncoder();
encoder.writeSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
encoder.writeSignature(dataBlobSignature);
encoder.writeValue(value);
return encoder.bytes.toBytes();
}
Expand All @@ -35,7 +55,8 @@ Uint8List encodeDataBlob(Object value) {
/// ints, doubles, booleans, and strings. See [decodeLibraryBlob] for a
/// description of the format.
///
/// The first four bytes of the file (in hex) are FE 52 57 44.
/// The first four bytes of the file (in hex) are FE 52 57 44; see
/// [dataBlobSignature].
///
/// See also:
///
Expand All @@ -45,7 +66,7 @@ Uint8List encodeDataBlob(Object value) {
/// * [parseDataFile], which parses the text variant of this format.
Object decodeDataBlob(Uint8List bytes) {
final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
decoder.expectSignature(<int>[0xFE, 0x52, 0x57, 0x44]);
decoder.expectSignature(dataBlobSignature);
final Object result = decoder.readValue();
if (!decoder.finished) {
throw const FormatException('Unexpected trailing bytes after value.');
Expand All @@ -63,7 +84,7 @@ Object decodeDataBlob(Uint8List bytes) {
/// * [parseLibraryFile], which parses the text variant of this format.
Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
final _BlobEncoder encoder = _BlobEncoder();
encoder.writeSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
encoder.writeSignature(libraryBlobSignature);
encoder.writeLibrary(value);
return encoder.bytes.toBytes();
}
Expand All @@ -78,7 +99,8 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
/// using a one-byte tag to identify types when necessary, and using 64 bit
/// integers to encode lengths when necessary.
///
/// The first four bytes of the file (in hex) are FE 52 46 57.
/// The first four bytes of the file (in hex) are FE 52 46 57; see
/// [libraryBlobSignature].
///
/// Primitives in this format are as follows:
///
Expand All @@ -93,7 +115,7 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
///
/// For example, the string "Hello" would be encoded as:
///
/// 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
/// 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
///
/// * Lists are encoded as an integer length, followed by that many values
/// back to back. When lists are of specific types (e.g. lists of imports),
Expand All @@ -102,13 +124,15 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
/// followed by the value (tagged lists). For example, a list of integers with
/// the values 1 and 2 in that order would be encoded as:
///
/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 02 00 00 00 00 00 00 00.
/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
/// 02 00 00 00 00 00 00 00
///
/// A list of arbitrary values that happens to contain one string "Hello"
/// would be encoded as follows; 0x04 is the tag for "String" (the full list
/// of tags is described below):
///
/// 01 00 00 00 00 00 00 00 04 05 00 00 00 00 00 00 00 48 65 6C 6C 6F
/// 01 00 00 00 00 00 00 00 04 05 00 00 00 00 00 00
/// 00 48 65 6C 6C 6F
///
/// A list of length zero is eight zero bytes with no additional payload.
///
Expand All @@ -123,7 +147,8 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
/// strings, so they are untagged) is encoded as follows (0x02 is the tag for
/// integers):
///
/// 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 02 0F 00 00 00 00 00 00 00
/// 01 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
/// 61 02 0F 00 00 00 00 00 00 00
///
/// Objects are encoded as follows:
///
Expand All @@ -134,7 +159,8 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
/// one of the subparts of the imported library name. For example, `import
/// a.b` is encoded as:
///
/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00 61 01 00 00 00 00 00 00 00 62
/// 02 00 00 00 00 00 00 00 01 00 00 00 00 00 00 00
/// 61 01 00 00 00 00 00 00 00 62
///
/// * Widget declarations are encoded as a string giving the declaration name,
/// an untagged map for the initial state, and finally the value that
Expand Down Expand Up @@ -204,11 +230,11 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
///
/// ...is encoded as follows (including the tag for the switch itself):
///
/// 0F 0A 01 00 00 00 00 00 00 00 61 03 00 00 00 00
/// 00 00 00 02 00 00 00 00 00 00 00 00 04 01 00 00
/// 00 00 00 00 00 7A 02 01 00 00 00 00 00 00 00 04
/// 01 00 00 00 00 00 00 00 6F 10 04 01 00 00 00 00
/// 00 00 00 64
/// 0F 0A 01 00 00 00 00 00 00 00 61 03 00 00 00 00
/// 00 00 00 02 00 00 00 00 00 00 00 00 04 01 00 00
/// 00 00 00 00 00 7A 02 01 00 00 00 00 00 00 00 04
/// 01 00 00 00 00 00 00 00 6F 10 04 01 00 00 00 00
/// 00 00 00 64
///
/// * Event handlers have the tag 0x0E, and are encoded as a string
/// ([EventHandler.eventName]) and an untagged map
Expand All @@ -227,7 +253,7 @@ Uint8List encodeLibraryBlob(RemoteWidgetLibrary value) {
/// * [parseDataFile], which parses the text variant of this format.
RemoteWidgetLibrary decodeLibraryBlob(Uint8List bytes) {
final _BlobDecoder decoder = _BlobDecoder(bytes.buffer.asByteData(bytes.offsetInBytes, bytes.lengthInBytes));
decoder.expectSignature(<int>[0xFE, 0x52, 0x46, 0x57]);
decoder.expectSignature(libraryBlobSignature);
final RemoteWidgetLibrary result = decoder.readLibrary();
if (!decoder.finished) {
throw const FormatException('Unexpected trailing bytes after constructors.');
Expand Down Expand Up @@ -293,9 +319,9 @@ class _BlobDecoder {
return bytes.getInt64(byteOffset, _blobEndian);
}

double _readDouble() {
double _readBinary64() {
final int byteOffset = _cursor;
_advance('double', 8);
_advance('binary64', 8);
return bytes.getFloat64(byteOffset, _blobEndian);
}

Expand Down Expand Up @@ -368,7 +394,7 @@ class _BlobDecoder {
case _msInt64:
return _readInt64();
case _msBinary64:
return _readDouble();
return _readBinary64();
case _msString:
return _readString();
case _msList:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ class ArgsReference extends Reference {
/// This class is an internal detail of the RFW [Runtime] and is generally not
/// used directly.
class BoundArgsReference extends Reference {
/// Wraps the given [parts] and [arguments] as an [ArgsReference].
/// Wraps the given [parts] and [arguments] as a [BoundArgsReference].
///
/// The parameters must not be mutated after the object is created.
///
Expand All @@ -301,7 +301,7 @@ class BoundArgsReference extends Reference {
/// Reference to the [DynamicContent] data that is passed into the widget (see
/// [Runtime.build]'s `data` argument).
class DataReference extends Reference {
/// Wraps the given [parts] as an [DataReference].
/// Wraps the given [parts] as a [DataReference].
///
/// The [parts] must not be mutated after the object is created.
const DataReference(List<Object> parts): super(parts);
Expand Down Expand Up @@ -349,7 +349,7 @@ class LoopReference extends Reference {
/// suppose that the widget itself has an [ArgsReference] that references
/// "args.a.baz". The "args.a" part identifies the aforementioned
/// [LoopReference], and so the resulting reference is actually to
/// "data.foo.bar.baz".
/// "loop0.foo.bar.baz".
///
/// In this example, the [LoopReference] to "loop0.foo.bar" would have its
/// [constructReference] method invoked by the runtime, with `["baz"]` as the
Expand Down Expand Up @@ -406,7 +406,7 @@ class BoundLoopReference extends Reference {
/// Now suppose that the widget itself has an [ArgsReference] that references
/// "args.a.baz". The "args.a" part identifies the aforementioned
/// [BoundLoopReference], and so the resulting reference is actually to
/// "data.foo.bar.baz".
/// "loop0.foo.bar.baz".
///
/// In this example, the [BoundLoopReference] to "loop0.foo.bar" would have
/// its [constructReference] method invoked by the runtime, with `["baz"]` as
Expand Down Expand Up @@ -567,6 +567,8 @@ class SetStateHandler extends AnyEventHandler {
///
/// Used to describe which libraries a remote widget libraries depends on. The
/// identified libraries can be local or remote. Import loops are invalid.
// TODO(ianh): eventually people will probably want a way to disambiguate imports
// with a prefix.
class Import extends BlobNode {
/// Wraps the given library [name] in an [Import] object.
const Import(this.name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -103,13 +103,17 @@ import 'model.dart';
/// of zero or more Unicode characters that do not include a newline, the quote
/// character used, or the backslash character, mixed with zero or more escapes.
///
/// Escapes are a backslash character followed another character, as follows:
/// Escapes are a backslash character followed by another character, as follows:
///
/// * `\b`: represents U+0008
/// * `\f`: represents U+000C
/// * `\n`: represents U+000A
/// * `\r`: represents U+000D
/// * `\t`: represents U+0009
/// * `"`: represents U+0022 (")
/// * `'`: represents U+0027 (')
/// * `/`: represents U+002F (/)
/// * `\`: represents U+005C (\)
/// * `\uXXXX`: represents the UTF-16 codepoint with code XXXX.
///
/// Characters outside the basic multilingual plane must be represented either
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -305,19 +305,19 @@ class _DynamicNode {
_value = value;
if (value is DynamicMap) {
for (final Object childKey in _children.keys) {
if (childKey is String && value.containsKey(childKey)) {
_children[childKey]!.update(value[childKey]!);
} else {
_children[childKey]!.update(missing);
Object? childValue;
if (childKey is String) {
childValue = value[childKey];
}
_children[childKey]!.update(childValue ?? missing);
}
} else if (value is DynamicList) {
for (final Object childKey in _children.keys) {
Object? childValue;
if (childKey is int && childKey >= 0 && childKey < value.length) {
_children[childKey]!.update(value[childKey]!);
} else {
_children[childKey]!.update(missing);
childValue = value[childKey];
}
_children[childKey]!.update(childValue ?? missing);
}
} else {
for (final _DynamicNode child in _children.values) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
import 'dart:ui' show FontFeature;

import 'package:flutter/gestures.dart' show DragStartBehavior;
import 'package:flutter/material.dart';
import 'package:flutter/material.dart' show Icons;
import 'package:flutter/widgets.dart';

import 'argument_decoders.dart';
import 'runtime.dart';
Expand Down Expand Up @@ -229,7 +230,7 @@ LocalWidgetLibrary createCoreWidgets() => LocalWidgetLibrary(_coreWidgetsDefinit
Map<String, LocalWidgetBuilder> get _coreWidgetsDefinitions => <String, LocalWidgetBuilder>{

// Keep these in alphabetical order.

'AnimationDefaults': (BuildContext context, DataSource source) {
return AnimationDefaults(
duration: ArgumentDecoders.duration(source, ['duration'], context),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class RemoteWidget extends StatefulWidget {
/// The [onEvent] argument is optional. When omitted, events are discarded.
const RemoteWidget({ Key? key, required this.runtime, required this.widget, required this.data, this.onEvent }) : super(key: key);

/// The [Runtime] to use to render the widget specified by [library] and [name].
/// The [Runtime] to use to render the widget specified by [widget].
///
/// This should update rarely (doing so is relatively expensive), but it is
/// fine to update it. For example, a client could update this on the fly when
Expand All @@ -32,9 +32,9 @@ class RemoteWidget extends StatefulWidget {
final Runtime runtime;

/// The name of the widget to display, and the library from which to obtain
/// in.
/// it.
///
/// The widget must be either declared in the specified library or one of its
/// The widget must be declared either in the specified library, or one of its
/// dependencies.
///
/// The data to show in the widget is specified using [data].
Expand Down
Loading

0 comments on commit 9d1e048

Please sign in to comment.