Skip to content

Commit dee75bf

Browse files
committed
v0.7.3
1 parent 90bd979 commit dee75bf

14 files changed

+607
-5
lines changed

CHANGELOG.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,6 @@
1+
## 0.7.3
2+
- Add MethodChannel and related wrappers
3+
14
## 0.7.2
25
- Add GestureDetector, Offset, Velocity, and related classes
36
- Support for Flutter 3.19

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -222,6 +222,14 @@ return RuntimeWidget(
222222

223223
You can also pass callbacks with `$Function`.
224224

225+
## Security and permissions
226+
227+
flutter_eval inherits dart_eval's
228+
[secure execution model](https://pub.dev/packages/dart_eval#security-and-permissions)
229+
which restricts access to the filesystem, network, and other sensitive APIs by default.
230+
flutter_eval extends this with security controls for MethodChannel and related APIs.
231+
To allow access to a MethodChannel, add a `MethodChannelPermission` to the `permissions` parameter of `EvalWidget`, `CompilerWidget`, or `RuntimeWidget`.
232+
225233
## Supported widgets and classes
226234

227235
Currently supported widgets and classes include:
@@ -312,7 +320,6 @@ class ExampleState extends State<Example> {
312320
313321
runtime = Runtime.ofProgram(program);
314322
runtime.addPlugin(flutterEvalPlugin);
315-
runtime.setup();
316323
}
317324
318325
@override
@@ -344,7 +351,6 @@ class ExampleState extends State<Example> {
344351
rootBundle.load('assets/out.evc').then((bytecode) => setState(() {
345352
runtime = Runtime(ByteData.sublistView(bytecode));
346353
runtime.addPlugin(flutterEvalPlugin);
347-
runtime!.setup();
348354
}));
349355
}
350356

lib/flutter_eval.dart

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,10 @@ import 'package:flutter_eval/src/rendering/box.dart';
5050
import 'package:flutter_eval/src/rendering/flex.dart';
5151
import 'package:flutter_eval/src/rendering/object.dart';
5252
import 'package:flutter_eval/src/rendering/proxy_box.dart';
53+
import 'package:flutter_eval/src/services.dart';
54+
import 'package:flutter_eval/src/services/binary_messenger.dart';
55+
import 'package:flutter_eval/src/services/message_codec.dart';
56+
import 'package:flutter_eval/src/services/platform_channel.dart';
5357
import 'package:flutter_eval/src/sky_engine/ui/geometry.dart';
5458
import 'package:flutter_eval/src/sky_engine/ui/image.dart';
5559
import 'package:flutter_eval/src/sky_engine/ui/painting.dart';
@@ -181,6 +185,10 @@ class FlutterEvalPlugin implements EvalPlugin {
181185
$DragUpdateDetails.$declaration,
182186
$DragEndDetails.$declaration,
183187
$DragDownDetails.$declaration,
188+
$BinaryMessenger.$declaration,
189+
$MethodCodec.$declaration,
190+
$MethodChannel.$declaration,
191+
$MethodCall.$declaration,
184192
];
185193

186194
for (final cls in classes) {
@@ -234,6 +242,9 @@ class FlutterEvalPlugin implements EvalPlugin {
234242
registry.addSource(
235243
DartSource('package:flutter/rendering.dart', renderingSource));
236244

245+
registry
246+
.addSource(DartSource('package:flutter/services.dart', servicesSource));
247+
237248
registry
238249
.addSource(DartSource('package:flutter/widgets.dart', widgetsSource));
239250
registry.addSource(DartSource(
@@ -438,6 +449,8 @@ class FlutterEvalPlugin implements EvalPlugin {
438449
'DragDownDetails.', $DragDownDetails.$new)
439450
..registerBridgeFunc('package:flutter/src/gestures/velocity_tracker.dart',
440451
'Velocity.', $Velocity.$new)
452+
..registerBridgeFunc('package:flutter/src/services/platform_channel.dart',
453+
'MethodChannel.', $MethodChannel.$new)
441454
..registerBridgeEnumValues('dart:ui', 'FontWeight', $FontWeight.$values)
442455
..registerBridgeEnumValues('dart:ui', 'FontStyle', $FontStyle.$values)
443456
..registerBridgeEnumValues(

lib/material.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
/// Bridge classes and wrappers for Flutter's Material widgets
22
library flutter_eval.material;
33

4-
export 'src/material/app_bar.dart' show $AppBar;
5-
export 'src/material/app.dart' show $MaterialApp;
4+
export 'src/material/app_bar.dart';
5+
export 'src/material/app.dart';
6+
export 'src/material/card.dart';
67
export 'src/material/scaffold.dart' show $Scaffold;

lib/services.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
library flutter_eval.services;
2+
3+
export 'src/services/binary_messenger.dart';
4+
export 'src/services/message_codec.dart';
5+
export 'src/services/platform_channel.dart';

lib/src/flutter_eval_security.dart

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,34 @@ class AssetPermission implements Permission {
3939
@override
4040
int get hashCode => matchPattern.hashCode ^ domains.hashCode;
4141
}
42+
43+
/// A permission that allows access to a Flutter method channel.
44+
class MethodChannelPermission implements Permission {
45+
/// The name of the method channel.
46+
final String channel;
47+
48+
/// Create a new method channel permission.
49+
const MethodChannelPermission(this.channel);
50+
51+
@override
52+
List<String> get domains => ['method_channel'];
53+
54+
@override
55+
bool match([Object? data]) {
56+
if (data is String) {
57+
return data == channel;
58+
}
59+
return false;
60+
}
61+
62+
@override
63+
bool operator ==(Object other) {
64+
if (other is MethodChannelPermission) {
65+
return other.channel == channel && other.domains == domains;
66+
}
67+
return false;
68+
}
69+
70+
@override
71+
int get hashCode => channel.hashCode ^ domains.hashCode;
72+
}

lib/src/material/card.dart

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,13 @@ import 'package:flutter_eval/src/painting/edge_insets.dart';
66
import 'package:flutter_eval/src/sky_engine/ui/painting.dart';
77
import 'package:flutter_eval/src/widgets/framework.dart';
88

9+
/// dart_eval wrapper for [Card]
910
class $Card implements $Instance {
11+
/// Compile-type type reference for [Card]
1012
static const $type = BridgeTypeRef(
1113
BridgeTypeSpec('package:flutter/src/material/card.dart', 'Card'));
1214

15+
/// Compile-type class declaration for [Card]
1316
static const $declaration = BridgeClassDef(
1417
BridgeClassType($type,
1518
isAbstract: false, $extends: $StatelessWidget$bridge.$type),
@@ -38,11 +41,13 @@ class $Card implements $Instance {
3841

3942
late final _superclass = $StatelessWidget.wrap($value);
4043

44+
/// Wrap a [Card] in a [$Card]
4145
$Card.wrap(this.$value);
4246

4347
@override
4448
final Card $value;
4549

50+
/// Wrapper for [Card.new] using [args]
4651
static $Value? $new(Runtime runtime, $Value? target, List<$Value?> args) {
4752
return $Card.wrap(Card(
4853
key: args[0]?.$value,

lib/src/rendering.dart

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
const renderingSource = '''
2+
library rendering;
3+
24
export 'src/rendering/box.dart';
35
export 'src/rendering/flex.dart';
46
export 'src/rendering/object.dart';

lib/src/services.dart

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
const servicesSource = '''
2+
library services;
3+
4+
export 'src/services/binary_messenger.dart';
5+
export 'src/services/message_codec.dart';
6+
export 'src/services/platform_channel.dart';
7+
''';
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
import 'package:dart_eval/dart_eval.dart';
2+
import 'package:dart_eval/dart_eval_bridge.dart';
3+
import 'package:dart_eval/stdlib/core.dart';
4+
import 'package:dart_eval/stdlib/typed_data.dart';
5+
import 'package:flutter/services.dart';
6+
7+
/// dart_eval wrapper for [BinaryMessenger]
8+
class $BinaryMessenger implements $Instance {
9+
/// Compile-type type reference for [BinaryMessenger]
10+
static const $type = BridgeTypeRef(BridgeTypeSpec(
11+
'package:flutter/src/services/binary_messenger.dart', 'BinaryMessenger'));
12+
13+
/// Compile-type class declaration for [BinaryMessenger]
14+
static const $declaration = BridgeClassDef(
15+
BridgeClassType($type, isAbstract: true),
16+
constructors: {},
17+
methods: {
18+
'send': BridgeMethodDef(BridgeFunctionDef(
19+
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.future)),
20+
params: [
21+
BridgeParameter('channel',
22+
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)), false),
23+
BridgeParameter(
24+
'data',
25+
BridgeTypeAnnotation(BridgeTypeRef(TypedDataTypes.byteData)),
26+
false)
27+
])),
28+
'setMessageHandler': BridgeMethodDef(BridgeFunctionDef(
29+
returns: BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.voidType)),
30+
params: [
31+
BridgeParameter('channel',
32+
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.string)), false),
33+
BridgeParameter('handler',
34+
BridgeTypeAnnotation(BridgeTypeRef(CoreTypes.function)), true)
35+
])),
36+
},
37+
wrap: true);
38+
39+
final $Instance _superclass;
40+
41+
/// Wrap a [BinaryMessenger] in a [$BinaryMessenger]
42+
$BinaryMessenger.wrap(this.$value) : _superclass = $Object($value);
43+
44+
@override
45+
final BinaryMessenger $value;
46+
47+
@override
48+
get $reified => $value;
49+
50+
@override
51+
$Value? $getProperty(Runtime runtime, String identifier) {
52+
switch (identifier) {
53+
case 'send':
54+
return __send;
55+
case 'setMessageHandler':
56+
return __setMessageHandler;
57+
default:
58+
return _superclass.$getProperty(runtime, identifier);
59+
}
60+
}
61+
62+
static const $Function __send = $Function(_send);
63+
static $Value? _send(Runtime runtime, $Value? target, List<$Value?> args) {
64+
final self = target as $BinaryMessenger;
65+
final channel = args[0] as $String;
66+
runtime.assertPermission('method_channel', channel);
67+
final data = args[1] as $ByteData?;
68+
final result = self.$value.send(channel.$value, data?.$value);
69+
if (result == null) return const $null();
70+
return $Future.wrap(result.then(
71+
(value) => value == null ? const $null() : $ByteData.wrap(value)));
72+
}
73+
74+
static const $Function __setMessageHandler = $Function(_setMessageHandler);
75+
static $Value? _setMessageHandler(
76+
Runtime runtime, $Value? target, List<$Value?> args) {
77+
final self = target as $BinaryMessenger;
78+
final channel = args[0] as $String;
79+
runtime.assertPermission('method_channel', channel);
80+
final handler = args[1] as EvalCallable?;
81+
self.$value.setMessageHandler(
82+
channel.$value,
83+
handler == null
84+
? null
85+
: (data) => handler.call(runtime, null,
86+
[data == null ? const $null() : $ByteData.wrap(data)])!.$value);
87+
return null;
88+
}
89+
90+
@override
91+
int $getRuntimeType(Runtime runtime) => runtime.lookupType($type.spec!);
92+
93+
@override
94+
void $setProperty(Runtime runtime, String identifier, $Value value) {
95+
return _superclass.$setProperty(runtime, identifier, value);
96+
}
97+
}

0 commit comments

Comments
 (0)