-
Notifications
You must be signed in to change notification settings - Fork 6k
[macOS,iOS] Expose channel buffers 'resize' and 'overflow' control co… #44848
[macOS,iOS] Expose channel buffers 'resize' and 'overflow' control co… #44848
Conversation
cc9f2b4
to
4248e61
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a couple quick suggestions.
shell/platform/darwin/common/framework/Source/FlutterChannels.mm
Outdated
Show resolved
Hide resolved
|
||
static void ResizeChannelBuffer(NSObject<FlutterBinaryMessenger>* binaryMessenger, | ||
NSString* channel, | ||
NSInteger newSize) { | ||
NSString* messageString = [NSString stringWithFormat:@"resize\r%@\r%@", channel, @(newSize)]; | ||
NSData* message = [messageString dataUsingEncoding:NSUTF8StringEncoding]; | ||
NSArray* args = @[ channel, [NSNumber numberWithInt:newSize] ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Alternatively:
NSArray* args = @[ channel, @(newSize) ];
@stuartmorgan do you know if we've formalised any preference here? I tend to use the shorter syntax, but I see both in the repo. Neither the Google nor the Chromium style guides appear to mention this on a brief scan.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIRC the Google style guide used to mention it; most likely it was culled under https://google.github.io/styleguide/objcguide.html#style-rules-should-pull-their-weight because there's no reason not to use the modern literal syntax.
Boxing with NSNumber should definitely use modern boxing syntax.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
When switching to the modern boxing syntax, for this particular code, it does not produce the same result at runtime.
As you can see in the change I pushed, the test is failing because the serialization result is not the same (and the result is wrong with the modern boxing syntax). I don't have enough ObjectiveC knowledge to understand why.
Failure message:
error: -[FlutterChannelsTest testResize] : OCProtocolMockObject(FlutterBinaryMessenger): unexpected method invoked: sendOnChannel:@"dev.flutter/channel-buffers" message:{length = 33, bytes = 0x07067265 73697a65 0c02070c 666c7574 ... 03000000 00000000 }
expected: sendOnChannel:@"dev.flutter/channel-buffers" message:{length = 29, bytes = 0x07067265 73697a65 0c02070c 666c7574 ... 65737403 03000000 } (NSInternalInconsistencyException)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The failing test says that the expected value was created with Dart code; why? It's not clear to me why you are asserting a specific representation here that comes from a different implementation with different integer semantics.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The encoded message is deserialized in the Dart side, the deserialization code is there:
engine/lib/ui/channel_buffers.dart
Lines 459 to 537 in 93e8901
void handleMessage(ByteData data) { | |
// We hard-code the deserialization here because the StandardMethodCodec class | |
// is part of the framework, not dart:ui. | |
final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); | |
if (bytes[0] == 0x07) { // 7 = value code for string | |
final int methodNameLength = bytes[1]; | |
if (methodNameLength >= 254) { // lengths greater than 253 have more elaborate encoding | |
throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); | |
} | |
int index = 2; // where we are in reading the bytes | |
final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength)); | |
index += methodNameLength; | |
switch (methodName) { | |
case 'resize': | |
if (bytes[index] != 0x0C) { // 12 = value code for list | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); | |
} | |
index += 1; | |
if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); | |
} | |
index += 1; | |
if (bytes[index] != 0x07) { // 7 = value code for string | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)"); | |
} | |
index += 1; | |
final int channelNameLength = bytes[index]; | |
if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); | |
} | |
index += 1; | |
final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); | |
if (channelName.contains('\u0000')) { | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)"); | |
} | |
index += channelNameLength; | |
if (bytes[index] != 0x03) { // 3 = value code for uint32 | |
throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)"); | |
} | |
index += 1; | |
resize(channelName, data.getUint32(index, Endian.host)); | |
case 'overflow': | |
if (bytes[index] != 0x0C) { // 12 = value code for list | |
throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); | |
} | |
index += 1; | |
if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. | |
throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); | |
} | |
index += 1; | |
if (bytes[index] != 0x07) { // 7 = value code for string | |
throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)"); | |
} | |
index += 1; | |
final int channelNameLength = bytes[index]; | |
if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding | |
throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); | |
} | |
index += 1; | |
final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); | |
index += channelNameLength; | |
if (bytes[index] != 0x01 && bytes[index] != 0x02) { // 1 = value code for true, 2 = value code for false | |
throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)"); | |
} | |
allowOverflow(channelName, bytes[index] == 0x01); | |
default: | |
throw Exception("Unrecognized method '$methodName' sent to $kControlChannelName"); | |
} | |
} else { | |
final List<String> parts = utf8.decode(bytes).split('\r'); | |
if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { | |
resize(parts[1], int.parse(parts[2])); | |
} else { | |
// If the message couldn't be decoded as UTF-8, a FormatException will | |
// have been thrown by utf8.decode() above. | |
throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); | |
} | |
} | |
} |
The test checks that the message that will be send to Dart as an array of bytes is exactly the expected one.
With the old boxing syntax (NSArray* args = @[ channel, [NSNumber numberWithInt:newSize] ];
), calling encodeMethodCall
returns the exact byte array.
The problem is not specific to the test, when running a Flutter app with a modified engine and a call to the resize API, the Dart side throws the following exception:
[ERROR:flutter/runtime/dart_vm_initializer.cc(41)] Unhandled Exception: Exception: Invalid arguments for 'resize' method sent to dev.flutter/channel-buffers (second argument must be an integer in the range 0 to 2147483647)
#0 ChannelBuffers.handleMessage (dart:ui/channel_buffers.dart:496:13)
#1 PlatformDispatcher._dispatchPlatformMessage (dart:ui/platform_dispatcher.dart:674:24)
#2 _dispatchPlatformMessage (dart:ui/hooks.dart:257:31)
Based on the Dart exception, I figured out that the modern boxing syntax is not treating the NSInteger as an int32 on 64bits platforms. I updated the PR with a cast to int and the encoded message is ok now.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
engine/lib/ui/channel_buffers.dart
Lines 459 to 537 in 93e8901
void handleMessage(ByteData data) { // We hard-code the deserialization here because the StandardMethodCodec class // is part of the framework, not dart:ui. final Uint8List bytes = data.buffer.asUint8List(data.offsetInBytes, data.lengthInBytes); if (bytes[0] == 0x07) { // 7 = value code for string final int methodNameLength = bytes[1]; if (methodNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception('Unrecognized message sent to $kControlChannelName (method name too long)'); } int index = 2; // where we are in reading the bytes final String methodName = utf8.decode(bytes.sublist(index, index + methodNameLength)); index += methodNameLength; switch (methodName) { case 'resize': if (bytes[index] != 0x0C) { // 12 = value code for list throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); } index += 1; if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (arguments must be a two-element list, channel name and new capacity)"); } index += 1; if (bytes[index] != 0x07) { // 7 = value code for string throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (first argument must be a string)"); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); if (channelName.contains('\u0000')) { throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (channel name must not contain any null bytes)"); } index += channelNameLength; if (bytes[index] != 0x03) { // 3 = value code for uint32 throw Exception("Invalid arguments for 'resize' method sent to $kControlChannelName (second argument must be an integer in the range 0 to 2147483647)"); } index += 1; resize(channelName, data.getUint32(index, Endian.host)); case 'overflow': if (bytes[index] != 0x0C) { // 12 = value code for list throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); } index += 1; if (bytes[index] < 0x02) { // We ignore extra arguments, in case we need to support them in the future, hence <2 rather than !=2. throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (arguments must be a two-element list, channel name and flag state)"); } index += 1; if (bytes[index] != 0x07) { // 7 = value code for string throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (first argument must be a string)"); } index += 1; final int channelNameLength = bytes[index]; if (channelNameLength >= 254) { // lengths greater than 253 have more elaborate encoding throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (channel name must be less than 254 characters long)"); } index += 1; final String channelName = utf8.decode(bytes.sublist(index, index + channelNameLength)); index += channelNameLength; if (bytes[index] != 0x01 && bytes[index] != 0x02) { // 1 = value code for true, 2 = value code for false throw Exception("Invalid arguments for 'overflow' method sent to $kControlChannelName (second argument must be a boolean)"); } allowOverflow(channelName, bytes[index] == 0x01); default: throw Exception("Unrecognized method '$methodName' sent to $kControlChannelName"); } } else { final List<String> parts = utf8.decode(bytes).split('\r'); if (parts.length == 1 + /*arity=*/2 && parts[0] == 'resize') { resize(parts[1], int.parse(parts[2])); } else { // If the message couldn't be decoded as UTF-8, a FormatException will // have been thrown by utf8.decode() above. throw Exception('Unrecognized message $parts sent to $kControlChannelName.'); } } }
That's unfortunate. Please put a comment on the cast that explains, and links to this code, because it's extremely unusual for channel code to have a requirement on int size. The normal encoder gracefully handles 32 and 64-bit values.
Based on the Dart exception, I figured out that the modern boxing syntax is not treating the NSInteger as an int32 on 64bits platforms.
That's correct, since NSInteger
is in fact 64-bit. Which should be fine, and only isn't because of unusual code at the other end of the channel.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I have updated the PR with a comment on the cast.
shell/platform/darwin/common/framework/Source/FlutterChannels.mm
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Headers/FlutterChannels.h
Outdated
Show resolved
Hide resolved
shell/platform/darwin/common/framework/Source/FlutterChannels.mm
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(Wrong button, sorry; that was meant to be a "request changes")
8efcace
to
af17331
Compare
e219eb1
to
fae64e6
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM with nits
* @param name The channel name. | ||
* @param messenger The binary messenger. | ||
*/ | ||
+ (void)setWarnsOnOverflow:(BOOL)allowed |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: (BOOL)warns
* @param allowed When true, the channel is expected to overflow and warning messages | ||
* will not be shown. | ||
*/ | ||
- (void)setWarnsOnOverflow:(BOOL)allowed; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
(BOOL)warns
// Cast newSize to int because the deserialization logic handles only 32 bits values, | ||
// see | ||
// https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L495. | ||
NSArray* args = @[ channel, @((int)newSize) ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@(static_cast<int>(newSize))
(We should never use C-style casts in a .mm
file as they are strictly worse than C++-style casts.)
*/ | ||
static void SetWarnsOnOverflow(NSObject<FlutterBinaryMessenger>* binaryMessenger, | ||
NSString* channel, | ||
BOOL allowed) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
warns
static void SetWarnsOnOverflow(NSObject<FlutterBinaryMessenger>* binaryMessenger, | ||
NSString* channel, | ||
BOOL allowed) { | ||
NSArray* args = @[ channel, @(allowed) ]; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why not just inline this into the call?
84b9468
to
7b9873f
Compare
7b9873f
to
30e8c5d
Compare
…sions) (#135041) Manual roll requested by [email protected] flutter/engine@589bde9...28f14e6 2023-09-19 [email protected] Roll Skia from d54cf63f051b to d756a2f5665d (5 revisions) (flutter/engine#46048) 2023-09-19 [email protected] Update CI to Chrome 117 (flutter/engine#45842) 2023-09-19 [email protected] [web] DOM objects implement JS object (flutter/engine#46047) 2023-09-19 [email protected] Roll Skia from 91adc7d289f7 to d54cf63f051b (3 revisions) (flutter/engine#46043) 2023-09-19 [email protected] Roll Skia from 1e84aa4509cd to 91adc7d289f7 (2 revisions) (flutter/engine#46040) 2023-09-19 [email protected] Roll Dart SDK from 8ad823c03f26 to e7cd697bd0e9 (2 revisions) (flutter/engine#46039) 2023-09-19 [email protected] Roll Fuchsia Mac SDK from qy5FU4y6sx1FscCpd... to 06g6i7-5u8O-FOTSi... (flutter/engine#46038) 2023-09-19 [email protected] Add more missing Skia #includes (flutter/engine#46034) 2023-09-19 [email protected] [macOS,iOS] Expose channel buffers 'resize' and 'overflow' control co… (flutter/engine#44848) 2023-09-19 [email protected] Roll Dart SDK from 5b0e7bda1379 to 8ad823c03f26 (3 revisions) (flutter/engine#46028) 2023-09-19 [email protected] Roll Skia from 744807d740c7 to 1e84aa4509cd (4 revisions) (flutter/engine#46026) Also rolling transitive DEPS: fuchsia/sdk/core/mac-amd64 from qy5FU4y6sx1F to 06g6i7-5u8O- If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
#46361) ## Description This PR is a follow-up to #44434 which introduces the `allowChannelBufferOverflow` function. It renames this function to `setWarnsOnChannelOverflow`. The previous naming was inspired by the framework side implementation. https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L574 During the review of the iOS/macOS implementation, #44848 (comment), we agreed that the existing naming is confusing because it implies that overflow will be allowed, but this not the case this function is used to enable/disable error messages when a channel overflows. ## Related Issue Follow-up for flutter/flutter#132386. ## Tests Updates 1 test.
#46360) ## Description This PR is a follow-up to #44636 which introduces the `set_allow_channel_overflow` function. It renames this function to `set_warns_on_channel_overflow`. The previous naming was inspired by the framework side implementation. https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L574 During the review of the iOS/macOS implementation, #44848 (comment), we agreed that the existing naming is confusing because it implies that overflow will be allowed, but this not the case this function is used to enable/disable error messages when a channel overflows. ## Related Issue Follow-up for flutter/flutter#132386. ## Tests Updates 2 tests.
…sions) (flutter#135041) Manual roll requested by [email protected] flutter/engine@589bde9...28f14e6 2023-09-19 [email protected] Roll Skia from d54cf63f051b to d756a2f5665d (5 revisions) (flutter/engine#46048) 2023-09-19 [email protected] Update CI to Chrome 117 (flutter/engine#45842) 2023-09-19 [email protected] [web] DOM objects implement JS object (flutter/engine#46047) 2023-09-19 [email protected] Roll Skia from 91adc7d289f7 to d54cf63f051b (3 revisions) (flutter/engine#46043) 2023-09-19 [email protected] Roll Skia from 1e84aa4509cd to 91adc7d289f7 (2 revisions) (flutter/engine#46040) 2023-09-19 [email protected] Roll Dart SDK from 8ad823c03f26 to e7cd697bd0e9 (2 revisions) (flutter/engine#46039) 2023-09-19 [email protected] Roll Fuchsia Mac SDK from qy5FU4y6sx1FscCpd... to 06g6i7-5u8O-FOTSi... (flutter/engine#46038) 2023-09-19 [email protected] Add more missing Skia #includes (flutter/engine#46034) 2023-09-19 [email protected] [macOS,iOS] Expose channel buffers 'resize' and 'overflow' control co… (flutter/engine#44848) 2023-09-19 [email protected] Roll Dart SDK from 5b0e7bda1379 to 8ad823c03f26 (3 revisions) (flutter/engine#46028) 2023-09-19 [email protected] Roll Skia from 744807d740c7 to 1e84aa4509cd (4 revisions) (flutter/engine#46026) Also rolling transitive DEPS: fuchsia/sdk/core/mac-amd64 from qy5FU4y6sx1F to 06g6i7-5u8O- If this roll has caused a breakage, revert this CL and stop the roller using the controls here: https://autoroll.skia.org/r/flutter-engine-flutter-autoroll Please CC [email protected],[email protected],[email protected] on the revert to ensure that a human is aware of the problem. To file a bug in Flutter: https://github.com/flutter/flutter/issues/new/choose To report a problem with the AutoRoller itself, please file a bug: https://bugs.chromium.org/p/skia/issues/entry?template=Autoroller+Bug Documentation for the AutoRoller is here: https://skia.googlesource.com/buildbot/+doc/main/autoroll/README.md
#44848) ## Description This PR update the helper function that invokes the control channel 'resize' command (previous implementation relied on a deprecated format). It also adds a similar helper function for the 'overflow' commands exposed by the control channel. See: https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L302-L309 ## Related Issue iOS and macOS implementation for flutter/flutter#132386 Similar implementations: - Android: #44434 - Linux: #44636 ## Tests Adds two tests.
#46361) ## Description This PR is a follow-up to #44434 which introduces the `allowChannelBufferOverflow` function. It renames this function to `setWarnsOnChannelOverflow`. The previous naming was inspired by the framework side implementation. https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L574 During the review of the iOS/macOS implementation, #44848 (comment), we agreed that the existing naming is confusing because it implies that overflow will be allowed, but this not the case this function is used to enable/disable error messages when a channel overflows. ## Related Issue Follow-up for flutter/flutter#132386. ## Tests Updates 1 test.
#46360) ## Description This PR is a follow-up to #44636 which introduces the `set_allow_channel_overflow` function. It renames this function to `set_warns_on_channel_overflow`. The previous naming was inspired by the framework side implementation. https://github.com/flutter/engine/blob/93e8901490e78c7ba7e319cce4470d9c6478c6dc/lib/ui/channel_buffers.dart#L574 During the review of the iOS/macOS implementation, #44848 (comment), we agreed that the existing naming is confusing because it implies that overflow will be allowed, but this not the case this function is used to enable/disable error messages when a channel overflows. ## Related Issue Follow-up for flutter/flutter#132386. ## Tests Updates 2 tests.
Description
This PR update the helper function that invokes the control channel 'resize' command (previous implementation relied on a deprecated format). It also adds a similar helper function for the 'overflow' commands exposed by the control channel.
See:
engine/lib/ui/channel_buffers.dart
Lines 302 to 309 in 93e8901
Related Issue
iOS and macOS implementation for flutter/flutter#132386
Similar implementations:
Tests
Adds two tests.