Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.

Commit ca68af2

Browse files
authored
Add a separate target for Dart coverter on FML types. (#14011)
The converters are still in a separate target that must be included manually. This allows targets that depend on FML but not Dart runtime not have to depend on the runtime. Adds a test that includes this target and tests image decompression from assets. There is also a test for the standalone DartConvertor in shell_unittests but not in fml_unittests be cause FML uni-tests cannot yet launch a VM. I will work on adding fixtures for those.
1 parent 27fa607 commit ca68af2

File tree

10 files changed

+358
-4
lines changed

10 files changed

+358
-4
lines changed

ci/licenses_golden/licenses_flutter

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,8 @@ FILE: ../../../flutter/fml/command_line_unittest.cc
9898
FILE: ../../../flutter/fml/compiler_specific.h
9999
FILE: ../../../flutter/fml/concurrent_message_loop.cc
100100
FILE: ../../../flutter/fml/concurrent_message_loop.h
101+
FILE: ../../../flutter/fml/dart/dart_converter.cc
102+
FILE: ../../../flutter/fml/dart/dart_converter.h
101103
FILE: ../../../flutter/fml/delayed_task.cc
102104
FILE: ../../../flutter/fml/delayed_task.h
103105
FILE: ../../../flutter/fml/eintr_wrapper.h

fml/BUILD.gn

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -249,8 +249,9 @@ executable("fml_unittests") {
249249
deps = [
250250
":fml_fixtures",
251251
"$flutter_root/fml",
252+
"$flutter_root/fml/dart",
253+
"$flutter_root/runtime:libdart",
252254
"$flutter_root/testing",
253-
"//third_party/dart/runtime:libdart_jit",
254255
]
255256
}
256257

fml/dart/BUILD.gn

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# Copyright 2013 The Flutter Authors. All rights reserved.
2+
# Use of this source code is governed by a BSD-style license that can be
3+
# found in the LICENSE file.
4+
5+
# Utilities for working with FML types with the Dart API. Targets that use FML
6+
# as well as Dart must explicitly include this target as FML itself cannot
7+
# depend on Dart.
8+
source_set("dart") {
9+
sources = [
10+
"dart_converter.cc",
11+
"dart_converter.h",
12+
]
13+
14+
public_deps = [
15+
"$flutter_root/fml",
16+
"$flutter_root/runtime:libdart",
17+
"//third_party/tonic",
18+
]
19+
}

fml/dart/dart_converter.cc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#include "flutter/fml/dart/dart_converter.h"
6+
7+
namespace fml {
8+
9+
//
10+
11+
} // namespace fml

fml/dart/dart_converter.h

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
// Copyright 2013 The Flutter Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style license that can be
3+
// found in the LICENSE file.
4+
5+
#ifndef FLUTTER_FML_DART_DART_CONVERTER_H_
6+
#define FLUTTER_FML_DART_DART_CONVERTER_H_
7+
8+
#include <memory>
9+
#include <vector>
10+
11+
#include "flutter/fml/mapping.h"
12+
#include "third_party/dart/runtime/include/dart_api.h"
13+
#include "third_party/tonic/converter/dart_converter.h"
14+
15+
namespace tonic {
16+
17+
using DartConverterMapping = std::unique_ptr<fml::Mapping>;
18+
19+
template <>
20+
struct DartConverter<DartConverterMapping> {
21+
static Dart_Handle ToDart(const DartConverterMapping& val) {
22+
if (!val) {
23+
return Dart_Null();
24+
}
25+
26+
auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize());
27+
28+
if (Dart_IsError(dart_list_handle)) {
29+
FML_LOG(ERROR) << "Error while attempting to allocate a list: "
30+
<< Dart_GetError(dart_list_handle);
31+
return dart_list_handle;
32+
}
33+
34+
if (val->GetSize() == 0) {
35+
// Nothing to copy. Just return the zero sized list.
36+
return dart_list_handle;
37+
}
38+
39+
auto result = Dart_ListSetAsBytes(dart_list_handle, // list
40+
0, // offset
41+
val->GetMapping(), // native array,
42+
val->GetSize() // length
43+
);
44+
45+
if (Dart_IsError(result)) {
46+
FML_LOG(ERROR) << "Error while attempting to create a Dart list: "
47+
<< Dart_GetError(result);
48+
return result;
49+
}
50+
51+
return dart_list_handle;
52+
}
53+
54+
static void SetReturnValue(Dart_NativeArguments args,
55+
const DartConverterMapping& val) {
56+
Dart_SetReturnValue(args, ToDart(val));
57+
}
58+
59+
static DartConverterMapping FromDart(Dart_Handle dart_list) {
60+
if (Dart_IsNull(dart_list)) {
61+
return nullptr;
62+
}
63+
64+
if (Dart_IsError(dart_list)) {
65+
FML_LOG(ERROR) << "Cannot convert an error handle to a list: "
66+
<< Dart_GetError(dart_list);
67+
return nullptr;
68+
}
69+
70+
if (!Dart_IsList(dart_list)) {
71+
FML_LOG(ERROR) << "Dart handle was not a list.";
72+
return nullptr;
73+
}
74+
75+
intptr_t length = 0;
76+
auto handle = Dart_ListLength(dart_list, &length);
77+
78+
if (Dart_IsError(handle)) {
79+
FML_LOG(ERROR) << "Could not get the length of the Dart list: "
80+
<< Dart_GetError(handle);
81+
return nullptr;
82+
}
83+
84+
if (length == 0) {
85+
// Return a valid zero sized mapping.
86+
return std::make_unique<fml::NonOwnedMapping>(nullptr, 0);
87+
}
88+
89+
auto mapping_buffer = ::malloc(length);
90+
91+
if (!mapping_buffer) {
92+
FML_LOG(ERROR)
93+
<< "Out of memory while attempting to allocate a mapping of size: "
94+
<< length;
95+
return nullptr;
96+
}
97+
98+
auto mapping = std::make_unique<fml::NonOwnedMapping>(
99+
static_cast<const uint8_t*>(mapping_buffer), length,
100+
[](const uint8_t* data, size_t size) {
101+
::free(const_cast<uint8_t*>(data));
102+
});
103+
104+
handle = Dart_ListGetAsBytes(
105+
dart_list, // list
106+
0, // offset
107+
static_cast<uint8_t*>(mapping_buffer), // native array
108+
length // length
109+
);
110+
111+
if (Dart_IsError(handle)) {
112+
FML_LOG(ERROR) << "Could not copy Dart list to native buffer: "
113+
<< Dart_GetError(handle);
114+
return nullptr;
115+
}
116+
117+
return mapping;
118+
}
119+
};
120+
121+
} // namespace tonic
122+
123+
#endif // FLUTTER_FML_DART_DART_CONVERTER_H_

shell/common/BUILD.gn

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ if (current_toolchain == host_toolchain) {
173173
":shell_unittests_gpu_configuration",
174174
"$flutter_root/common",
175175
"$flutter_root/flow",
176+
"$flutter_root/fml/dart",
176177
"$flutter_root/lib/ui:ui",
177178
"$flutter_root/shell",
178179
"$flutter_root/testing:dart",

shell/common/fixtures/shell_test.dart

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -121,3 +121,20 @@ void canAccessIsolateLaunchData() {
121121
}
122122

123123
void notifyMessage(String string) native 'NotifyMessage';
124+
125+
@pragma('vm:entry-point')
126+
void canConvertMappings() {
127+
sendFixtureMapping(getFixtureMapping());
128+
}
129+
130+
List<int> getFixtureMapping() native 'GetFixtureMapping';
131+
void sendFixtureMapping(List<int> list) native 'SendFixtureMapping';
132+
133+
@pragma('vm:entry-point')
134+
void canDecompressImageFromAsset() {
135+
decodeImageFromList(Uint8List.fromList(getFixtureImage()), (Image result) {
136+
notifyWidthHeight(result.width, result.height);
137+
});
138+
}
139+
140+
List<int> getFixtureImage() native 'GetFixtureImage';

shell/common/shell_unittests.cc

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@
1313
#include "flutter/flow/layers/picture_layer.h"
1414
#include "flutter/flow/layers/transform_layer.h"
1515
#include "flutter/fml/command_line.h"
16+
#include "flutter/fml/dart/dart_converter.h"
1617
#include "flutter/fml/make_copyable.h"
1718
#include "flutter/fml/message_loop.h"
1819
#include "flutter/fml/synchronization/count_down_latch.h"
@@ -1014,5 +1015,143 @@ TEST_F(ShellTest, Screenshot) {
10141015
DestroyShell(std::move(shell));
10151016
}
10161017

1018+
enum class MemsetPatternOp {
1019+
kMemsetPatternOpSetBuffer,
1020+
kMemsetPatternOpCheckBuffer,
1021+
};
1022+
1023+
//------------------------------------------------------------------------------
1024+
/// @brief Depending on the operation, either scribbles a known pattern
1025+
/// into the buffer or checks if that pattern is present in an
1026+
/// existing buffer. This is a portable variant of the
1027+
/// memset_pattern class of methods that also happen to do assert
1028+
/// that the same pattern exists.
1029+
///
1030+
/// @param buffer The buffer
1031+
/// @param[in] size The size
1032+
/// @param[in] op The operation
1033+
///
1034+
/// @return If the result of the operation was a success.
1035+
///
1036+
static bool MemsetPatternSetOrCheck(uint8_t* buffer,
1037+
size_t size,
1038+
MemsetPatternOp op) {
1039+
if (buffer == nullptr) {
1040+
return false;
1041+
}
1042+
1043+
auto pattern = reinterpret_cast<const uint8_t*>("dErP");
1044+
constexpr auto pattern_length = 4;
1045+
1046+
uint8_t* start = buffer;
1047+
uint8_t* p = buffer;
1048+
1049+
while ((start + size) - p >= pattern_length) {
1050+
switch (op) {
1051+
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
1052+
memmove(p, pattern, pattern_length);
1053+
break;
1054+
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
1055+
if (memcmp(pattern, p, pattern_length) != 0) {
1056+
return false;
1057+
}
1058+
break;
1059+
};
1060+
p += pattern_length;
1061+
}
1062+
1063+
if ((start + size) - p != 0) {
1064+
switch (op) {
1065+
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
1066+
memmove(p, pattern, (start + size) - p);
1067+
break;
1068+
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
1069+
if (memcmp(pattern, p, (start + size) - p) != 0) {
1070+
return false;
1071+
}
1072+
break;
1073+
}
1074+
}
1075+
1076+
return true;
1077+
}
1078+
1079+
TEST_F(ShellTest, CanConvertToAndFromMappings) {
1080+
const size_t buffer_size = 2 << 20;
1081+
1082+
uint8_t* buffer = static_cast<uint8_t*>(::malloc(buffer_size));
1083+
ASSERT_NE(buffer, nullptr);
1084+
ASSERT_TRUE(MemsetPatternSetOrCheck(
1085+
buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer));
1086+
1087+
std::unique_ptr<fml::Mapping> mapping =
1088+
std::make_unique<fml::NonOwnedMapping>(
1089+
buffer, buffer_size, [](const uint8_t* buffer, size_t size) {
1090+
::free(const_cast<uint8_t*>(buffer));
1091+
});
1092+
1093+
ASSERT_EQ(mapping->GetSize(), buffer_size);
1094+
1095+
fml::AutoResetWaitableEvent latch;
1096+
AddNativeCallback(
1097+
"SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1098+
auto mapping_from_dart =
1099+
tonic::DartConverter<std::unique_ptr<fml::Mapping>>::FromDart(
1100+
Dart_GetNativeArgument(args, 0));
1101+
ASSERT_NE(mapping_from_dart, nullptr);
1102+
ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size);
1103+
ASSERT_TRUE(MemsetPatternSetOrCheck(
1104+
const_cast<uint8_t*>(mapping_from_dart->GetMapping()), // buffer
1105+
mapping_from_dart->GetSize(), // size
1106+
MemsetPatternOp::kMemsetPatternOpCheckBuffer // op
1107+
));
1108+
latch.Signal();
1109+
}));
1110+
1111+
AddNativeCallback(
1112+
"GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
1113+
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
1114+
args, mapping);
1115+
}));
1116+
1117+
auto settings = CreateSettingsForFixture();
1118+
auto configuration = RunConfiguration::InferFromSettings(settings);
1119+
configuration.SetEntrypoint("canConvertMappings");
1120+
std::unique_ptr<Shell> shell = CreateShell(settings);
1121+
ASSERT_NE(shell.get(), nullptr);
1122+
RunEngine(shell.get(), std::move(configuration));
1123+
latch.Wait();
1124+
DestroyShell(std::move(shell));
1125+
}
1126+
1127+
TEST_F(ShellTest, CanDecompressImageFromAsset) {
1128+
fml::AutoResetWaitableEvent latch;
1129+
AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
1130+
auto width = tonic::DartConverter<int>::FromDart(
1131+
Dart_GetNativeArgument(args, 0));
1132+
auto height = tonic::DartConverter<int>::FromDart(
1133+
Dart_GetNativeArgument(args, 1));
1134+
ASSERT_EQ(width, 100);
1135+
ASSERT_EQ(height, 100);
1136+
latch.Signal();
1137+
}));
1138+
1139+
AddNativeCallback(
1140+
"GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) {
1141+
auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png");
1142+
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
1143+
args, fixture);
1144+
}));
1145+
1146+
auto settings = CreateSettingsForFixture();
1147+
auto configuration = RunConfiguration::InferFromSettings(settings);
1148+
configuration.SetEntrypoint("canDecompressImageFromAsset");
1149+
std::unique_ptr<Shell> shell = CreateShell(settings);
1150+
ASSERT_NE(shell.get(), nullptr);
1151+
RunEngine(shell.get(), std::move(configuration));
1152+
latch.Wait();
1153+
DestroyShell(std::move(shell));
1154+
}
1155+
10171156
} // namespace testing
10181157
} // namespace flutter

testing/testing.cc

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,5 +49,9 @@ fml::UniqueFD OpenFixture(std::string fixture_name) {
4949
return fixture_fd;
5050
}
5151

52+
std::unique_ptr<fml::Mapping> OpenFixtureAsMapping(std::string fixture_name) {
53+
return fml::FileMapping::CreateReadOnly(OpenFixture(fixture_name));
54+
}
55+
5256
} // namespace testing
5357
} // namespace flutter

0 commit comments

Comments
 (0)