Skip to content
This repository was archived by the owner on Feb 25, 2025. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions ci/licenses_golden/licenses_flutter
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,8 @@ FILE: ../../../flutter/fml/command_line_unittest.cc
FILE: ../../../flutter/fml/compiler_specific.h
FILE: ../../../flutter/fml/concurrent_message_loop.cc
FILE: ../../../flutter/fml/concurrent_message_loop.h
FILE: ../../../flutter/fml/dart/dart_converter.cc
FILE: ../../../flutter/fml/dart/dart_converter.h
FILE: ../../../flutter/fml/delayed_task.cc
FILE: ../../../flutter/fml/delayed_task.h
FILE: ../../../flutter/fml/eintr_wrapper.h
Expand Down
3 changes: 2 additions & 1 deletion fml/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -249,8 +249,9 @@ executable("fml_unittests") {
deps = [
":fml_fixtures",
"$flutter_root/fml",
"$flutter_root/fml/dart",
"$flutter_root/runtime:libdart",
"$flutter_root/testing",
"//third_party/dart/runtime:libdart_jit",
]
}

Expand Down
19 changes: 19 additions & 0 deletions fml/dart/BUILD.gn
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
# Copyright 2013 The Flutter Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

# Utilities for working with FML types with the Dart API. Targets that use FML
# as well as Dart must explicitly include this target as FML itself cannot
# depend on Dart.
source_set("dart") {
sources = [
"dart_converter.cc",
"dart_converter.h",
]

public_deps = [
"$flutter_root/fml",
"$flutter_root/runtime:libdart",
"//third_party/tonic",
]
}
11 changes: 11 additions & 0 deletions fml/dart/dart_converter.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "flutter/fml/dart/dart_converter.h"

namespace fml {

//

} // namespace fml
123 changes: 123 additions & 0 deletions fml/dart/dart_converter.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
// Copyright 2013 The Flutter Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#ifndef FLUTTER_FML_DART_DART_CONVERTER_H_
#define FLUTTER_FML_DART_DART_CONVERTER_H_

#include <memory>
#include <vector>

#include "flutter/fml/mapping.h"
#include "third_party/dart/runtime/include/dart_api.h"
#include "third_party/tonic/converter/dart_converter.h"

namespace tonic {

using DartConverterMapping = std::unique_ptr<fml::Mapping>;

template <>
struct DartConverter<DartConverterMapping> {
static Dart_Handle ToDart(const DartConverterMapping& val) {
if (!val) {
return Dart_Null();
}

auto dart_list_handle = Dart_NewListOf(Dart_CoreType_Int, val->GetSize());

if (Dart_IsError(dart_list_handle)) {
FML_LOG(ERROR) << "Error while attempting to allocate a list: "
<< Dart_GetError(dart_list_handle);
return dart_list_handle;
}

if (val->GetSize() == 0) {
// Nothing to copy. Just return the zero sized list.
return dart_list_handle;
}

auto result = Dart_ListSetAsBytes(dart_list_handle, // list
0, // offset
val->GetMapping(), // native array,
val->GetSize() // length
);

if (Dart_IsError(result)) {
FML_LOG(ERROR) << "Error while attempting to create a Dart list: "
<< Dart_GetError(result);
return result;
}

return dart_list_handle;
}

static void SetReturnValue(Dart_NativeArguments args,
const DartConverterMapping& val) {
Dart_SetReturnValue(args, ToDart(val));
}

static DartConverterMapping FromDart(Dart_Handle dart_list) {
if (Dart_IsNull(dart_list)) {
return nullptr;
}

if (Dart_IsError(dart_list)) {
FML_LOG(ERROR) << "Cannot convert an error handle to a list: "
<< Dart_GetError(dart_list);
return nullptr;
}

if (!Dart_IsList(dart_list)) {
FML_LOG(ERROR) << "Dart handle was not a list.";
return nullptr;
}

intptr_t length = 0;
auto handle = Dart_ListLength(dart_list, &length);

if (Dart_IsError(handle)) {
FML_LOG(ERROR) << "Could not get the length of the Dart list: "
<< Dart_GetError(handle);
return nullptr;
}

if (length == 0) {
// Return a valid zero sized mapping.
return std::make_unique<fml::NonOwnedMapping>(nullptr, 0);
}

auto mapping_buffer = ::malloc(length);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For a zero-length list this can return null, in which case we'll handle as an OOM below.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Good catch. Returns a valid zero sized mapping now.


if (!mapping_buffer) {
FML_LOG(ERROR)
<< "Out of memory while attempting to allocate a mapping of size: "
<< length;
return nullptr;
}

auto mapping = std::make_unique<fml::NonOwnedMapping>(
static_cast<const uint8_t*>(mapping_buffer), length,
[](const uint8_t* data, size_t size) {
::free(const_cast<uint8_t*>(data));
});

handle = Dart_ListGetAsBytes(
dart_list, // list
0, // offset
static_cast<uint8_t*>(mapping_buffer), // native array
length // length
);

if (Dart_IsError(handle)) {
FML_LOG(ERROR) << "Could not copy Dart list to native buffer: "
<< Dart_GetError(handle);
return nullptr;
}

return mapping;
}
};

} // namespace tonic

#endif // FLUTTER_FML_DART_DART_CONVERTER_H_
1 change: 1 addition & 0 deletions shell/common/BUILD.gn
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,7 @@ if (current_toolchain == host_toolchain) {
":shell_unittests_gpu_configuration",
"$flutter_root/common",
"$flutter_root/flow",
"$flutter_root/fml/dart",
"$flutter_root/lib/ui:ui",
"$flutter_root/shell",
"$flutter_root/testing:dart",
Expand Down
17 changes: 17 additions & 0 deletions shell/common/fixtures/shell_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -121,3 +121,20 @@ void canAccessIsolateLaunchData() {
}

void notifyMessage(String string) native 'NotifyMessage';

@pragma('vm:entry-point')
void canConvertMappings() {
sendFixtureMapping(getFixtureMapping());
}

List<int> getFixtureMapping() native 'GetFixtureMapping';
void sendFixtureMapping(List<int> list) native 'SendFixtureMapping';

@pragma('vm:entry-point')
void canDecompressImageFromAsset() {
decodeImageFromList(Uint8List.fromList(getFixtureImage()), (Image result) {
notifyWidthHeight(result.width, result.height);
});
}

List<int> getFixtureImage() native 'GetFixtureImage';
139 changes: 139 additions & 0 deletions shell/common/shell_unittests.cc
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@
#include "flutter/flow/layers/picture_layer.h"
#include "flutter/flow/layers/transform_layer.h"
#include "flutter/fml/command_line.h"
#include "flutter/fml/dart/dart_converter.h"
#include "flutter/fml/make_copyable.h"
#include "flutter/fml/message_loop.h"
#include "flutter/fml/synchronization/count_down_latch.h"
Expand Down Expand Up @@ -1014,5 +1015,143 @@ TEST_F(ShellTest, Screenshot) {
DestroyShell(std::move(shell));
}

enum class MemsetPatternOp {
kMemsetPatternOpSetBuffer,
kMemsetPatternOpCheckBuffer,
};

//------------------------------------------------------------------------------
/// @brief Depending on the operation, either scribbles a known pattern
/// into the buffer or checks if that pattern is present in an
/// existing buffer. This is a portable variant of the
/// memset_pattern class of methods that also happen to do assert
/// that the same pattern exists.
///
/// @param buffer The buffer
/// @param[in] size The size
/// @param[in] op The operation
///
/// @return If the result of the operation was a success.
///
static bool MemsetPatternSetOrCheck(uint8_t* buffer,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add a short doc comment, since it's a utility. Particularly, document that if set_or_checkis true, you set, otherwise you check.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done. Made the bool an enum.

size_t size,
MemsetPatternOp op) {
if (buffer == nullptr) {
return false;
}

auto pattern = reinterpret_cast<const uint8_t*>("dErP");
constexpr auto pattern_length = 4;

uint8_t* start = buffer;
uint8_t* p = buffer;

while ((start + size) - p >= pattern_length) {
switch (op) {
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
memmove(p, pattern, pattern_length);
break;
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
if (memcmp(pattern, p, pattern_length) != 0) {
return false;
}
break;
};
p += pattern_length;
}

if ((start + size) - p != 0) {
switch (op) {
case MemsetPatternOp::kMemsetPatternOpSetBuffer:
memmove(p, pattern, (start + size) - p);
break;
case MemsetPatternOp::kMemsetPatternOpCheckBuffer:
if (memcmp(pattern, p, (start + size) - p) != 0) {
return false;
}
break;
}
}

return true;
}

TEST_F(ShellTest, CanConvertToAndFromMappings) {
const size_t buffer_size = 2 << 20;

uint8_t* buffer = static_cast<uint8_t*>(::malloc(buffer_size));
ASSERT_NE(buffer, nullptr);
ASSERT_TRUE(MemsetPatternSetOrCheck(
buffer, buffer_size, MemsetPatternOp::kMemsetPatternOpSetBuffer));

std::unique_ptr<fml::Mapping> mapping =
std::make_unique<fml::NonOwnedMapping>(
buffer, buffer_size, [](const uint8_t* buffer, size_t size) {
::free(const_cast<uint8_t*>(buffer));
});

ASSERT_EQ(mapping->GetSize(), buffer_size);

fml::AutoResetWaitableEvent latch;
AddNativeCallback(
"SendFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
auto mapping_from_dart =
tonic::DartConverter<std::unique_ptr<fml::Mapping>>::FromDart(
Dart_GetNativeArgument(args, 0));
ASSERT_NE(mapping_from_dart, nullptr);
ASSERT_EQ(mapping_from_dart->GetSize(), buffer_size);
ASSERT_TRUE(MemsetPatternSetOrCheck(
const_cast<uint8_t*>(mapping_from_dart->GetMapping()), // buffer
mapping_from_dart->GetSize(), // size
MemsetPatternOp::kMemsetPatternOpCheckBuffer // op
));
latch.Signal();
}));

AddNativeCallback(
"GetFixtureMapping", CREATE_NATIVE_ENTRY([&](auto args) {
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
args, mapping);
}));

auto settings = CreateSettingsForFixture();
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("canConvertMappings");
std::unique_ptr<Shell> shell = CreateShell(settings);
ASSERT_NE(shell.get(), nullptr);
RunEngine(shell.get(), std::move(configuration));
latch.Wait();
DestroyShell(std::move(shell));
}

TEST_F(ShellTest, CanDecompressImageFromAsset) {
fml::AutoResetWaitableEvent latch;
AddNativeCallback("NotifyWidthHeight", CREATE_NATIVE_ENTRY([&](auto args) {
auto width = tonic::DartConverter<int>::FromDart(
Dart_GetNativeArgument(args, 0));
auto height = tonic::DartConverter<int>::FromDart(
Dart_GetNativeArgument(args, 1));
ASSERT_EQ(width, 100);
ASSERT_EQ(height, 100);
latch.Signal();
}));

AddNativeCallback(
"GetFixtureImage", CREATE_NATIVE_ENTRY([](auto args) {
auto fixture = OpenFixtureAsMapping("shelltest_screenshot.png");
tonic::DartConverter<tonic::DartConverterMapping>::SetReturnValue(
args, fixture);
}));

auto settings = CreateSettingsForFixture();
auto configuration = RunConfiguration::InferFromSettings(settings);
configuration.SetEntrypoint("canDecompressImageFromAsset");
std::unique_ptr<Shell> shell = CreateShell(settings);
ASSERT_NE(shell.get(), nullptr);
RunEngine(shell.get(), std::move(configuration));
latch.Wait();
DestroyShell(std::move(shell));
}

} // namespace testing
} // namespace flutter
4 changes: 4 additions & 0 deletions testing/testing.cc
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,9 @@ fml::UniqueFD OpenFixture(std::string fixture_name) {
return fixture_fd;
}

std::unique_ptr<fml::Mapping> OpenFixtureAsMapping(std::string fixture_name) {
return fml::FileMapping::CreateReadOnly(OpenFixture(fixture_name));
}

} // namespace testing
} // namespace flutter
Loading