diff options
author | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-03-08 10:28:10 +0100 |
---|---|---|
committer | Allan Sandfeld Jensen <allan.jensen@qt.io> | 2017-03-20 13:40:30 +0000 |
commit | e733310db58160074f574c429d48f8308c0afe17 (patch) | |
tree | f8aef4b7e62a69928dbcf880620eece20f98c6df /chromium/mojo | |
parent | 2f583e4aec1ae3a86fa047829c96b310dc12ecdf (diff) | |
download | qtwebengine-chromium-e733310db58160074f574c429d48f8308c0afe17.tar.gz |
BASELINE: Update Chromium to 56.0.2924.122
Change-Id: I4e04de8f47e47e501c46ed934c76a431c6337ced
Reviewed-by: Michael BrĂ¼ning <michael.bruning@qt.io>
Diffstat (limited to 'chromium/mojo')
92 files changed, 2129 insertions, 1403 deletions
diff --git a/chromium/mojo/BUILD.gn b/chromium/mojo/BUILD.gn index def24ea224d..9440e669df5 100644 --- a/chromium/mojo/BUILD.gn +++ b/chromium/mojo/BUILD.gn @@ -20,7 +20,7 @@ group("mojo") { deps += [ "//mojo/android" ] } - deps += [ "//services/shell:all" ] + deps += [ "//services/service_manager:all" ] } group("tests") { @@ -36,8 +36,8 @@ group("tests") { "//mojo/edk/test:mojo_public_bindings_unittests", "//mojo/edk/test:mojo_public_system_perftests", "//mojo/edk/test:mojo_public_system_unittests", - "//services/shell/public/cpp/tests:mojo_public_application_unittests", - "//services/shell/runner/host:mojo_runner_host_unittests", - "//services/shell/tests", + "//services/service_manager/public/cpp/tests:mojo_public_application_unittests", + "//services/service_manager/runner/host:mojo_runner_host_unittests", + "//services/service_manager/tests", ] } diff --git a/chromium/mojo/DEPS b/chromium/mojo/DEPS index a6592005c85..49d7fd30d62 100644 --- a/chromium/mojo/DEPS +++ b/chromium/mojo/DEPS @@ -3,5 +3,5 @@ include_rules = [ "+build", "+testing", - "+services/shell", + "+services/service_manager", ] diff --git a/chromium/mojo/android/BUILD.gn b/chromium/mojo/android/BUILD.gn index f9bdb0a49ec..e09fb353fff 100644 --- a/chromium/mojo/android/BUILD.gn +++ b/chromium/mojo/android/BUILD.gn @@ -68,7 +68,7 @@ android_library("system_java") { deps = [ "//base:base_java", - "//mojo/public/java:system", + "//mojo/public/java:system_java", ] } @@ -105,8 +105,8 @@ android_library("mojo_javatests") { "//mojo/public/interfaces/bindings/tests:test_interfaces_java", "//mojo/public/interfaces/bindings/tests:test_mojom_import2_java", "//mojo/public/interfaces/bindings/tests:test_mojom_import_java", - "//mojo/public/java:bindings", - "//mojo/public/java:system", + "//mojo/public/java:bindings_java", + "//mojo/public/java:system_java", ] data = [ @@ -145,7 +145,7 @@ instrumentation_test_apk("mojo_test_apk") { ":system_java", "//base:base_java", "//mojo/public/interfaces/bindings/tests:test_interfaces", - "//mojo/public/java:bindings", + "//mojo/public/java:bindings_java", ] shared_libraries = [ ":mojo_java_unittests" ] apk_name = "MojoTest" diff --git a/chromium/mojo/common/DEPS b/chromium/mojo/common/DEPS index 588c68dbff9..f3e298ffc22 100644 --- a/chromium/mojo/common/DEPS +++ b/chromium/mojo/common/DEPS @@ -1,7 +1,7 @@ include_rules = [ # common must not depend on embedder. "-mojo", - "+services/shell/public/cpp", + "+services/service_manager/public/cpp", "+mojo/common", "+mojo/public", ] diff --git a/chromium/mojo/common/common_custom_types_struct_traits.cc b/chromium/mojo/common/common_custom_types_struct_traits.cc index c18f793b793..c3b7ef20008 100644 --- a/chromium/mojo/common/common_custom_types_struct_traits.cc +++ b/chromium/mojo/common/common_custom_types_struct_traits.cc @@ -9,35 +9,25 @@ namespace mojo { // static -mojo::ConstCArray<uint16_t> -StructTraits<mojo::common::mojom::String16DataView, base::string16>::data( - const base::string16& str) { - return mojo::ConstCArray<uint16_t>( - str.size(), reinterpret_cast<const uint16_t*>(str.data())); -} - -// static -bool StructTraits<mojo::common::mojom::String16DataView, base::string16>::Read( - mojo::common::mojom::String16DataView data, +bool StructTraits<common::mojom::String16DataView, base::string16>::Read( + common::mojom::String16DataView data, base::string16* out) { - mojo::ArrayDataView<uint16_t> view; + ArrayDataView<uint16_t> view; data.GetDataDataView(&view); - if (view.is_null()) - return false; out->assign(reinterpret_cast<const base::char16*>(view.data()), view.size()); return true; } // static const std::vector<uint32_t>& -StructTraits<mojo::common::mojom::VersionDataView, base::Version>::components( +StructTraits<common::mojom::VersionDataView, base::Version>::components( const base::Version& version) { return version.components(); } // static -bool StructTraits<mojo::common::mojom::VersionDataView, base::Version>::Read( - mojo::common::mojom::VersionDataView data, +bool StructTraits<common::mojom::VersionDataView, base::Version>::Read( + common::mojom::VersionDataView data, base::Version* out) { std::vector<uint32_t> components; if (!data.ReadComponents(&components)) @@ -48,10 +38,10 @@ bool StructTraits<mojo::common::mojom::VersionDataView, base::Version>::Read( } // static -bool StructTraits<mojo::common::mojom::UnguessableTokenDataView, - base::UnguessableToken>:: - Read(mojo::common::mojom::UnguessableTokenDataView data, - base::UnguessableToken* out) { +bool StructTraits< + common::mojom::UnguessableTokenDataView, + base::UnguessableToken>::Read(common::mojom::UnguessableTokenDataView data, + base::UnguessableToken* out) { uint64_t high = data.high(); uint64_t low = data.low(); diff --git a/chromium/mojo/common/common_custom_types_struct_traits.h b/chromium/mojo/common/common_custom_types_struct_traits.h index 37be5409074..b99e15ffc9d 100644 --- a/chromium/mojo/common/common_custom_types_struct_traits.h +++ b/chromium/mojo/common/common_custom_types_struct_traits.h @@ -14,23 +14,25 @@ namespace mojo { template <> -struct StructTraits<mojo::common::mojom::String16DataView, base::string16> { - static mojo::ConstCArray<uint16_t> data(const base::string16& str); - static bool Read(mojo::common::mojom::String16DataView data, - base::string16* out); +struct StructTraits<common::mojom::String16DataView, base::string16> { + static ConstCArray<uint16_t> data(const base::string16& str) { + return ConstCArray<uint16_t>(str.size(), + reinterpret_cast<const uint16_t*>(str.data())); + } + + static bool Read(common::mojom::String16DataView data, base::string16* out); }; template <> -struct StructTraits<mojo::common::mojom::VersionDataView, base::Version> { +struct StructTraits<common::mojom::VersionDataView, base::Version> { static bool IsNull(const base::Version& version) { - return !version.IsValid(); + return !version.IsValid(); } static void SetToNull(base::Version* out) { - *out = base::Version(std::string()); + *out = base::Version(std::string()); } static const std::vector<uint32_t>& components(const base::Version& version); - static bool Read(mojo::common::mojom::VersionDataView data, - base::Version* out); + static bool Read(common::mojom::VersionDataView data, base::Version* out); }; // If base::UnguessableToken is no longer 128 bits, the logic below and the @@ -39,8 +41,8 @@ static_assert(sizeof(base::UnguessableToken) == 2 * sizeof(uint64_t), "base::UnguessableToken should be of size 2 * sizeof(uint64_t)."); template <> -struct StructTraits<mojo::common::mojom::UnguessableTokenDataView, - base::UnguessableToken> { +struct StructTraits<common::mojom::UnguessableTokenDataView, + base::UnguessableToken> { static uint64_t high(const base::UnguessableToken& token) { return token.GetHighForSerialization(); } @@ -49,17 +51,17 @@ struct StructTraits<mojo::common::mojom::UnguessableTokenDataView, return token.GetLowForSerialization(); } - static bool Read(mojo::common::mojom::UnguessableTokenDataView data, + static bool Read(common::mojom::UnguessableTokenDataView data, base::UnguessableToken* out); }; template <> -struct StructTraits<mojo::common::mojom::TimeDeltaDataView, base::TimeDelta> { +struct StructTraits<common::mojom::TimeDeltaDataView, base::TimeDelta> { static int64_t microseconds(const base::TimeDelta& delta) { return delta.InMicroseconds(); } - static bool Read(mojo::common::mojom::TimeDeltaDataView data, + static bool Read(common::mojom::TimeDeltaDataView data, base::TimeDelta* delta) { *delta = base::TimeDelta::FromMicroseconds(data.microseconds()); return true; diff --git a/chromium/mojo/edk/embedder/README.md b/chromium/mojo/edk/embedder/README.md index f976fcbd8e6..ea03e4376d9 100644 --- a/chromium/mojo/edk/embedder/README.md +++ b/chromium/mojo/edk/embedder/README.md @@ -6,8 +6,8 @@ implementation. It should be used by code running on top of the system-level APIs to set up the Mojo environment (instead of directly instantiating things from src/mojo/edk/system). -Example uses: Mojo shell, to set up the Mojo environment for Mojo apps; Chromium -code, to set up the Mojo IPC system for use between processes. Note that most -code should use the Mojo Public API (under src/mojo/public) instead. The -Embedder API should only be used to initialize the environment, set up the +Example uses: Service Manager, to set up the Mojo environment for Services; +Chromium code, to set up the Mojo IPC system for use between processes. Note +that most code should use the Mojo Public API (under src/mojo/public) instead. +The Embedder API should only be used to initialize the environment, set up the initial MessagePipe between two processes, etc. diff --git a/chromium/mojo/edk/embedder/embedder.cc b/chromium/mojo/edk/embedder/embedder.cc index 3508ba3fc47..758d96c6580 100644 --- a/chromium/mojo/edk/embedder/embedder.cc +++ b/chromium/mojo/edk/embedder/embedder.cc @@ -77,9 +77,20 @@ void SetParentPipeHandleFromCommandLine() { } ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe) { + return ConnectToPeerProcess(std::move(pipe), GenerateRandomToken()); +} + +ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe, + const std::string& peer_token) { CHECK(internal::g_process_delegate); DCHECK(pipe.is_valid()); - return internal::g_core->ConnectToPeerProcess(std::move(pipe)); + DCHECK(!peer_token.empty()); + return internal::g_core->ConnectToPeerProcess(std::move(pipe), peer_token); +} + +void ClosePeerConnection(const std::string& peer_token) { + CHECK(internal::g_process_delegate); + return internal::g_core->ClosePeerConnection(peer_token); } void Init() { diff --git a/chromium/mojo/edk/embedder/embedder.h b/chromium/mojo/edk/embedder/embedder.h index 8cb44a119cb..e673aff2cd3 100644 --- a/chromium/mojo/edk/embedder/embedder.h +++ b/chromium/mojo/edk/embedder/embedder.h @@ -77,6 +77,18 @@ MOJO_SYSTEM_IMPL_EXPORT void SetParentPipeHandleFromCommandLine(); MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe); +// Called to connect to a peer process. This should be called only if there +// is no common ancestor for the processes involved within this mojo system. +// Both processes must call this function, each passing one end of a platform +// channel. This returns one end of a message pipe to each process. |peer_token| +// may be passed to ClosePeerConnection() to close the connection. +MOJO_SYSTEM_IMPL_EXPORT ScopedMessagePipeHandle +ConnectToPeerProcess(ScopedPlatformHandle pipe, const std::string& peer_token); + +// Closes a connection to a peer process created by ConnectToPeerProcess() +// where the same |peer_token| was used. +MOJO_SYSTEM_IMPL_EXPORT void ClosePeerConnection(const std::string& peer_token); + // Must be called first, or just after setting configuration parameters, to // initialize the (global, singleton) system. MOJO_SYSTEM_IMPL_EXPORT void Init(); diff --git a/chromium/mojo/edk/embedder/embedder_unittest.cc b/chromium/mojo/edk/embedder/embedder_unittest.cc index 127a74f0957..aea0f078079 100644 --- a/chromium/mojo/edk/embedder/embedder_unittest.cc +++ b/chromium/mojo/edk/embedder/embedder_unittest.cc @@ -10,6 +10,7 @@ #include <utility> +#include "base/base_paths.h" #include "base/bind.h" #include "base/command_line.h" #include "base/files/file.h" @@ -17,13 +18,18 @@ #include "base/macros.h" #include "base/memory/shared_memory.h" #include "base/message_loop/message_loop.h" +#include "base/path_service.h" #include "base/process/process_handle.h" +#include "base/run_loop.h" #include "base/synchronization/waitable_event.h" #include "base/test/test_timeouts.h" +#include "mojo/edk/embedder/named_platform_handle.h" +#include "mojo/edk/embedder/named_platform_handle_utils.h" #include "mojo/edk/embedder/platform_channel_pair.h" #include "mojo/edk/embedder/test_embedder.h" #include "mojo/edk/system/test_utils.h" #include "mojo/edk/test/mojo_test_base.h" +#include "mojo/edk/test/scoped_ipc_support.h" #include "mojo/public/c/system/core.h" #include "mojo/public/cpp/system/handle.h" #include "mojo/public/cpp/system/message_pipe.h" @@ -557,6 +563,118 @@ DEFINE_TEST_CLIENT_TEST_WITH_PIPE(MultiprocessMixMachAndFdsClient, EmbedderTest, #endif // !defined(OS_IOS) +NamedPlatformHandle GenerateChannelName() { +#if defined(OS_POSIX) + base::FilePath temp_dir; + CHECK(base::PathService::Get(base::DIR_TEMP, &temp_dir)); + return NamedPlatformHandle( + temp_dir.AppendASCII(GenerateRandomToken()).value()); +#else + return NamedPlatformHandle(GenerateRandomToken()); +#endif +} + +void CreateClientHandleOnIoThread(const NamedPlatformHandle& named_handle, + ScopedPlatformHandle* output) { + *output = CreateClientHandle(named_handle); +} + +#if defined(OS_ANDROID) +// Disabled on Android as suspected source of flake. https://crbug.com/666356. +#define MAYBE_ClosePendingPeerConnection DISABLED_ClosePendingPeerConnection +#else +#define MAYBE_ClosePendingPeerConnection ClosePendingPeerConnection +#endif +TEST_F(EmbedderTest, MAYBE_ClosePendingPeerConnection) { + NamedPlatformHandle named_handle = GenerateChannelName(); + std::string peer_token = GenerateRandomToken(); + ScopedMessagePipeHandle server_pipe = + ConnectToPeerProcess(CreateServerHandle(named_handle), peer_token); + ClosePeerConnection(peer_token); + EXPECT_EQ(MOJO_RESULT_OK, + Wait(server_pipe.get(), MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr)); + base::MessageLoop message_loop; + base::RunLoop run_loop; + ScopedPlatformHandle client_handle; + // Closing the channel involves posting a task to the IO thread to do the + // work. By the time the local message pipe has been observerd as closed, + // that task will have been posted. Therefore, a task to create the client + // connection should be handled after the channel is closed. + test::GetIoTaskRunner()->PostTaskAndReply( + FROM_HERE, + base::Bind(&CreateClientHandleOnIoThread, named_handle, &client_handle), + run_loop.QuitClosure()); + run_loop.Run(); + EXPECT_FALSE(client_handle.is_valid()); +} + +#if !defined(OS_IOS) + +#if defined(OS_ANDROID) +// Disabled on Android as suspected source of flake. https://crbug.com/666356. +#define MAYBE_ClosePipeToConnectedPeer DISABLED_ClosePipeToConnectedPeer +#else +#define MAYBE_ClosePipeToConnectedPeer ClosePipeToConnectedPeer +#endif +TEST_F(EmbedderTest, MAYBE_ClosePipeToConnectedPeer) { + set_launch_type(LaunchType::PEER); + auto& controller = StartClient("ClosePipeToConnectedPeerClient"); + MojoHandle server_mp = controller.pipe(); + // 1. Write a message to |server_mp| (attaching nothing). + WriteMessage(server_mp, "hello"); + + // 2. Read a message from |server_mp|. + EXPECT_EQ("world!", ReadMessage(server_mp)); + + controller.ClosePeerConnection(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr)); + + EXPECT_EQ(0, controller.WaitForShutdown()); +} + +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectedPeerClient, EmbedderTest, + client_mp) { + // 1. Read the first message from |client_mp|. + EXPECT_EQ("hello", ReadMessage(client_mp)); + + // 2. Write a message to |client_mp| (attaching nothing). + WriteMessage(client_mp, "world!"); + + ASSERT_EQ(MOJO_RESULT_OK, + MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr)); +} + +#if defined(OS_ANDROID) +// Disabled on Android as suspected source of flake. https://crbug.com/666356. +#define MAYBE_ClosePipeToConnectingPeer DISABLED_ClosePipeToConnectingPeer +#else +#define MAYBE_ClosePipeToConnectingPeer ClosePipeToConnectingPeer +#endif +TEST_F(EmbedderTest, MAYBE_ClosePipeToConnectingPeer) { + set_launch_type(LaunchType::PEER); + auto& controller = StartClient("ClosePipeToConnectingPeerClient"); + controller.ClosePeerConnection(); + + MojoHandle server_mp = controller.pipe(); + + EXPECT_EQ(MOJO_RESULT_OK, MojoWait(server_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr)); + + EXPECT_EQ(0, controller.WaitForShutdown()); +} + +DEFINE_TEST_CLIENT_TEST_WITH_PIPE(ClosePipeToConnectingPeerClient, EmbedderTest, + client_mp) { + ASSERT_EQ(MOJO_RESULT_OK, MojoWait(client_mp, MOJO_HANDLE_SIGNAL_PEER_CLOSED, + MOJO_DEADLINE_INDEFINITE, nullptr)); +} + +#endif // !defined(OS_IOS) + } // namespace } // namespace edk } // namespace mojo diff --git a/chromium/mojo/edk/embedder/named_platform_channel_pair.h b/chromium/mojo/edk/embedder/named_platform_channel_pair.h index a238a4564d4..71688861cc0 100644 --- a/chromium/mojo/edk/embedder/named_platform_channel_pair.h +++ b/chromium/mojo/edk/embedder/named_platform_channel_pair.h @@ -28,7 +28,16 @@ namespace edk { // resolve it into a client handle. class MOJO_SYSTEM_IMPL_EXPORT NamedPlatformChannelPair { public: - NamedPlatformChannelPair(); + struct Options { +#if defined(OS_WIN) + // If non-empty, a security descriptor to use when creating the pipe. If + // empty, a default security descriptor will be used. See + // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc. + base::string16 security_descriptor; +#endif + }; + + NamedPlatformChannelPair(const Options& options = {}); ~NamedPlatformChannelPair(); // Note: It is NOT acceptable to use this handle as a generic pipe channel. It diff --git a/chromium/mojo/edk/embedder/named_platform_channel_pair_win.cc b/chromium/mojo/edk/embedder/named_platform_channel_pair_win.cc index 39c16b9b1dc..96589ff5cfb 100644 --- a/chromium/mojo/edk/embedder/named_platform_channel_pair_win.cc +++ b/chromium/mojo/edk/embedder/named_platform_channel_pair_win.cc @@ -35,9 +35,13 @@ std::wstring GeneratePipeName() { } // namespace -NamedPlatformChannelPair::NamedPlatformChannelPair() +NamedPlatformChannelPair::NamedPlatformChannelPair( + const NamedPlatformChannelPair::Options& options) : pipe_handle_(GeneratePipeName()) { - server_handle_ = CreateServerHandle(pipe_handle_, true); + CreateServerHandleOptions server_handle_options; + server_handle_options.security_descriptor = options.security_descriptor; + server_handle_options.enforce_uniqueness = true; + server_handle_ = CreateServerHandle(pipe_handle_, server_handle_options); PCHECK(server_handle_.is_valid()); } diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils.h b/chromium/mojo/edk/embedder/named_platform_handle_utils.h index 6b856bb52f0..f13e1765fab 100644 --- a/chromium/mojo/edk/embedder/named_platform_handle_utils.h +++ b/chromium/mojo/edk/embedder/named_platform_handle_utils.h @@ -9,21 +9,37 @@ #include "mojo/edk/embedder/scoped_platform_handle.h" #include "mojo/edk/system/system_impl_export.h" +#if defined(OS_WIN) +#include "base/strings/string16.h" +#endif + namespace mojo { namespace edk { struct NamedPlatformHandle; +struct CreateServerHandleOptions { +#if defined(OS_WIN) + // If true, creating a server handle will fail if another pipe with the same + // name exists. + bool enforce_uniqueness = true; + + // If non-empty, a security descriptor to use when creating the pipe. If + // empty, a default security descriptor will be used. See + // kDefaultSecurityDescriptor in named_platform_handle_utils_win.cc. + base::string16 security_descriptor; +#endif +}; + // Creates a client platform handle from |handle|. This may block until |handle| // is ready to receive connections. MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle CreateClientHandle(const NamedPlatformHandle& handle); -// Creates a server platform handle from |handle|. On Windows, if -// |enforce_uniqueness| is true, this will fail if another pipe with the same -// name exists. On other platforms, |enforce_uniqueness| must be false. +// Creates a server platform handle from |handle|. MOJO_SYSTEM_IMPL_EXPORT ScopedPlatformHandle -CreateServerHandle(const NamedPlatformHandle& handle, bool enforce_uniqueness); +CreateServerHandle(const NamedPlatformHandle& handle, + const CreateServerHandleOptions& options = {}); } // namespace edk } // namespace mojo diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc b/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc index 97572c05903..4d9e8eece02 100644 --- a/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc +++ b/chromium/mojo/edk/embedder/named_platform_handle_utils_posix.cc @@ -99,9 +99,9 @@ ScopedPlatformHandle CreateClientHandle( return handle; } -ScopedPlatformHandle CreateServerHandle(const NamedPlatformHandle& named_handle, - bool enforce_uniqueness) { - CHECK(!enforce_uniqueness); +ScopedPlatformHandle CreateServerHandle( + const NamedPlatformHandle& named_handle, + const CreateServerHandleOptions& options) { if (!named_handle.is_valid()) return ScopedPlatformHandle(); diff --git a/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc b/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc index ccf506217ae..a145847fd04 100644 --- a/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc +++ b/chromium/mojo/edk/embedder/named_platform_handle_utils_win.cc @@ -15,6 +15,18 @@ namespace mojo { namespace edk { +namespace { + +// A DACL to grant: +// GA = Generic All +// access to: +// SY = LOCAL_SYSTEM +// BA = BUILTIN_ADMINISTRATORS +// OW = OWNER_RIGHTS +constexpr base::char16 kDefaultSecurityDescriptor[] = + L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)"; + +} // namespace ScopedPlatformHandle CreateClientHandle( const NamedPlatformHandle& named_handle) { @@ -37,31 +49,32 @@ ScopedPlatformHandle CreateClientHandle( 0, // No sharing. nullptr, OPEN_EXISTING, kFlags, nullptr))); // No template file. - PCHECK(handle.is_valid()); + // The server may have stopped accepting a connection between the + // WaitNamedPipe() and CreateFile(). If this occurs, an invalid handle is + // returned. + DPLOG_IF(ERROR, !handle.is_valid()) + << "Named pipe " << named_handle.pipe_name() + << " could not be opened after WaitNamedPipe succeeded"; return handle; } -ScopedPlatformHandle CreateServerHandle(const NamedPlatformHandle& named_handle, - bool enforce_uniqueness) { +ScopedPlatformHandle CreateServerHandle( + const NamedPlatformHandle& named_handle, + const CreateServerHandleOptions& options) { if (!named_handle.is_valid()) return ScopedPlatformHandle(); PSECURITY_DESCRIPTOR security_desc = nullptr; ULONG security_desc_len = 0; - // Create a DACL to grant: - // GA = Generic All - // access to: - // SY = LOCAL_SYSTEM - // BA = BUILTIN_ADMINISTRATORS - // OW = OWNER_RIGHTS PCHECK(ConvertStringSecurityDescriptorToSecurityDescriptor( - L"D:(A;;GA;;;SY)(A;;GA;;;BA)(A;;GA;;;OW)", SDDL_REVISION_1, - &security_desc, &security_desc_len)); + options.security_descriptor.empty() ? kDefaultSecurityDescriptor + : options.security_descriptor.c_str(), + SDDL_REVISION_1, &security_desc, &security_desc_len)); std::unique_ptr<void, decltype(::LocalFree)*> p(security_desc, ::LocalFree); SECURITY_ATTRIBUTES security_attributes = {sizeof(SECURITY_ATTRIBUTES), security_desc, FALSE}; - const DWORD kOpenMode = enforce_uniqueness + const DWORD kOpenMode = options.enforce_uniqueness ? PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED | FILE_FLAG_FIRST_PIPE_INSTANCE : PIPE_ACCESS_DUPLEX | FILE_FLAG_OVERLAPPED; @@ -69,9 +82,9 @@ ScopedPlatformHandle CreateServerHandle(const NamedPlatformHandle& named_handle, PIPE_TYPE_BYTE | PIPE_READMODE_BYTE | PIPE_REJECT_REMOTE_CLIENTS; PlatformHandle handle( CreateNamedPipeW(named_handle.pipe_name().c_str(), kOpenMode, kPipeMode, - enforce_uniqueness ? 1 : 255, // Max instances. - 4096, // Out buffer size. - 4096, // In buffer size. + options.enforce_uniqueness ? 1 : 255, // Max instances. + 4096, // Out buffer size. + 4096, // In buffer size. 5000, // Timeout in milliseconds. &security_attributes)); handle.needs_connection = true; diff --git a/chromium/mojo/edk/js/BUILD.gn b/chromium/mojo/edk/js/BUILD.gn index 429a8c09355..9a36497f565 100644 --- a/chromium/mojo/edk/js/BUILD.gn +++ b/chromium/mojo/edk/js/BUILD.gn @@ -16,7 +16,7 @@ group("tests") { ] } -source_set("js") { +component("js") { sources = [ "core.cc", "core.h", @@ -25,6 +25,7 @@ source_set("js") { "handle.cc", "handle.h", "handle_close_observer.h", + "js_export.h", "mojo_runner_delegate.cc", "mojo_runner_delegate.h", "support.cc", @@ -44,4 +45,5 @@ source_set("js") { deps = [ "//mojo/public/cpp/system", ] + defines = [ "MOJO_JS_IMPLEMENTATION" ] } diff --git a/chromium/mojo/edk/js/core.h b/chromium/mojo/edk/js/core.h index ca203ea76a3..97ef5dfd811 100644 --- a/chromium/mojo/edk/js/core.h +++ b/chromium/mojo/edk/js/core.h @@ -5,13 +5,14 @@ #ifndef MOJO_EDK_JS_CORE_H_ #define MOJO_EDK_JS_CORE_H_ +#include "mojo/edk/js/js_export.h" #include "v8/include/v8.h" namespace mojo { namespace edk { namespace js { -class Core { +class MOJO_JS_EXPORT Core { public: static const char kModuleName[]; static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); diff --git a/chromium/mojo/edk/js/handle.cc b/chromium/mojo/edk/js/handle.cc index 9f9f1619cc7..7da8e9fa196 100644 --- a/chromium/mojo/edk/js/handle.cc +++ b/chromium/mojo/edk/js/handle.cc @@ -37,7 +37,8 @@ void HandleWrapper::NotifyCloseObservers() { if (!handle_.is_valid()) return; - FOR_EACH_OBSERVER(HandleCloseObserver, close_observers_, OnWillCloseHandle()); + for (auto& observer : close_observers_) + observer.OnWillCloseHandle(); } } // namespace js diff --git a/chromium/mojo/edk/js/handle.h b/chromium/mojo/edk/js/handle.h index 2470fac9f11..60652ed8c2c 100644 --- a/chromium/mojo/edk/js/handle.h +++ b/chromium/mojo/edk/js/handle.h @@ -11,6 +11,7 @@ #include "gin/converter.h" #include "gin/handle.h" #include "gin/wrappable.h" +#include "mojo/edk/js/js_export.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -21,7 +22,7 @@ class HandleCloseObserver; // Wrapper for mojo Handles exposed to JavaScript. This ensures the Handle // is Closed when its JS object is garbage collected. -class HandleWrapper : public gin::Wrappable<HandleWrapper> { +class MOJO_JS_EXPORT HandleWrapper : public gin::Wrappable<HandleWrapper> { public: static gin::WrapperInfo kWrapperInfo; @@ -56,16 +57,16 @@ namespace gin { // MojoHandle, since that will do a simple int32_t conversion. It's unfortunate // there's no way to prevent against accidental use. // TODO(mpcomplete): define converters for all Handle subtypes. -template<> -struct Converter<mojo::Handle> { +template <> +struct MOJO_JS_EXPORT Converter<mojo::Handle> { static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, const mojo::Handle& val); static bool FromV8(v8::Isolate* isolate, v8::Handle<v8::Value> val, mojo::Handle* out); }; -template<> -struct Converter<mojo::MessagePipeHandle> { +template <> +struct MOJO_JS_EXPORT Converter<mojo::MessagePipeHandle> { static v8::Handle<v8::Value> ToV8(v8::Isolate* isolate, mojo::MessagePipeHandle val); static bool FromV8(v8::Isolate* isolate, @@ -76,7 +77,7 @@ struct Converter<mojo::MessagePipeHandle> { // We need to specialize the normal gin::Handle converter in order to handle // converting |null| to a wrapper for an empty mojo::Handle. template <> -struct Converter<gin::Handle<mojo::edk::js::HandleWrapper>> { +struct MOJO_JS_EXPORT Converter<gin::Handle<mojo::edk::js::HandleWrapper>> { static v8::Handle<v8::Value> ToV8( v8::Isolate* isolate, const gin::Handle<mojo::edk::js::HandleWrapper>& val) { diff --git a/chromium/mojo/edk/js/js_export.h b/chromium/mojo/edk/js/js_export.h new file mode 100644 index 00000000000..179113ce4fe --- /dev/null +++ b/chromium/mojo/edk/js/js_export.h @@ -0,0 +1,32 @@ +// Copyright 2016 The Chromium 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 MOJO_EDK_JS_JS_EXPORT_H_ +#define MOJO_EDK_JS_JS_EXPORT_H_ + +// Defines MOJO_JS_EXPORT so that functionality implemented by //mojo/edk/js can +// be exported to consumers. + +#if defined(COMPONENT_BUILD) +#if defined(WIN32) + +#if defined(MOJO_JS_IMPLEMENTATION) +#define MOJO_JS_EXPORT __declspec(dllexport) +#else +#define MOJO_JS_EXPORT __declspec(dllimport) +#endif // defined(MOJO_JS_IMPLEMENTATION) + +#else // defined(WIN32) +#if defined(MOJO_JS_IMPLEMENTATION) +#define MOJO_JS_EXPORT __attribute__((visibility("default"))) +#else +#define MOJO_JS_EXPORT +#endif +#endif + +#else // defined(COMPONENT_BUILD) +#define MOJO_JS_EXPORT +#endif + +#endif // MOJO_EDK_JS_JS_EXPORT_H_ diff --git a/chromium/mojo/edk/js/mojo_runner_delegate.h b/chromium/mojo/edk/js/mojo_runner_delegate.h index a76460c56f4..9ab325c8dfb 100644 --- a/chromium/mojo/edk/js/mojo_runner_delegate.h +++ b/chromium/mojo/edk/js/mojo_runner_delegate.h @@ -7,13 +7,14 @@ #include "base/macros.h" #include "gin/modules/module_runner_delegate.h" +#include "mojo/edk/js/js_export.h" #include "mojo/public/c/system/core.h" namespace mojo { namespace edk { namespace js { -class MojoRunnerDelegate : public gin::ModuleRunnerDelegate { +class MOJO_JS_EXPORT MojoRunnerDelegate : public gin::ModuleRunnerDelegate { public: MojoRunnerDelegate(); ~MojoRunnerDelegate() override; diff --git a/chromium/mojo/edk/js/support.h b/chromium/mojo/edk/js/support.h index c57cf7a1a89..551f5ac0a82 100644 --- a/chromium/mojo/edk/js/support.h +++ b/chromium/mojo/edk/js/support.h @@ -5,13 +5,14 @@ #ifndef MOJO_EDK_JS_SUPPORT_H_ #define MOJO_EDK_JS_SUPPORT_H_ +#include "mojo/edk/js/js_export.h" #include "v8/include/v8.h" namespace mojo { namespace edk { namespace js { -class Support { +class MOJO_JS_EXPORT Support { public: static const char kModuleName[]; static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); diff --git a/chromium/mojo/edk/js/threading.h b/chromium/mojo/edk/js/threading.h index 10404d5bc77..653d0765a69 100644 --- a/chromium/mojo/edk/js/threading.h +++ b/chromium/mojo/edk/js/threading.h @@ -6,13 +6,14 @@ #define MOJO_EDK_JS_THREADING_H_ #include "gin/public/wrapper_info.h" +#include "mojo/edk/js/js_export.h" #include "v8/include/v8.h" namespace mojo { namespace edk { namespace js { -class Threading { +class MOJO_JS_EXPORT Threading { public: static const char kModuleName[]; static v8::Local<v8::Value> GetModule(v8::Isolate* isolate); diff --git a/chromium/mojo/edk/system/broker_host.cc b/chromium/mojo/edk/system/broker_host.cc index a5a5f9cf04b..28d5157321d 100644 --- a/chromium/mojo/edk/system/broker_host.cc +++ b/chromium/mojo/edk/system/broker_host.cc @@ -19,14 +19,6 @@ namespace mojo { namespace edk { -namespace { - -// To prevent abuse, limit the maximum size of shared memory buffers. -// TODO(rockot): Re-consider this limit, or do something smarter. -const uint32_t kMaxSharedBufferSize = 16 * 1024 * 1024; - -} // namespace - BrokerHost::BrokerHost(base::ProcessHandle client_process, ScopedPlatformHandle platform_handle) #if defined(OS_WIN) @@ -107,17 +99,13 @@ void BrokerHost::SendNamedChannel(const base::StringPiece16& pipe_name) { #endif // defined(OS_WIN) void BrokerHost::OnBufferRequest(uint32_t num_bytes) { - scoped_refptr<PlatformSharedBuffer> buffer; scoped_refptr<PlatformSharedBuffer> read_only_buffer; - if (num_bytes <= kMaxSharedBufferSize) { - buffer = PlatformSharedBuffer::Create(num_bytes); - if (buffer) - read_only_buffer = buffer->CreateReadOnlyDuplicate(); - if (!read_only_buffer) - buffer = nullptr; - } else { - LOG(ERROR) << "Shared buffer request too large: " << num_bytes; - } + scoped_refptr<PlatformSharedBuffer> buffer = + PlatformSharedBuffer::Create(num_bytes); + if (buffer) + read_only_buffer = buffer->CreateReadOnlyDuplicate(); + if (!read_only_buffer) + buffer = nullptr; Channel::MessagePtr message = CreateBrokerMessage( BrokerMessageType::BUFFER_RESPONSE, buffer ? 2 : 0, nullptr); diff --git a/chromium/mojo/edk/system/broker_win.cc b/chromium/mojo/edk/system/broker_win.cc index e6d72e6716e..063282c146f 100644 --- a/chromium/mojo/edk/system/broker_win.cc +++ b/chromium/mojo/edk/system/broker_win.cc @@ -7,6 +7,7 @@ #include <limits> #include <utility> +#include "base/debug/alias.h" #include "base/numerics/safe_conversions.h" #include "base/strings/string_piece.h" #include "mojo/edk/embedder/named_platform_handle.h" @@ -63,6 +64,11 @@ Channel::MessagePtr WaitForBrokerMessage(PlatformHandle platform_handle, Channel::Message::Deserialize(buffer, static_cast<size_t>(bytes_read)); if (!message || message->payload_size() < sizeof(BrokerMessageHeader)) { LOG(ERROR) << "Invalid broker message"; + + base::debug::Alias(&buffer[0]); + base::debug::Alias(&bytes_read); + base::debug::Alias(message.get()); + CHECK(false); return nullptr; } @@ -70,6 +76,11 @@ Channel::MessagePtr WaitForBrokerMessage(PlatformHandle platform_handle, reinterpret_cast<const BrokerMessageHeader*>(message->payload()); if (header->type != expected_type) { LOG(ERROR) << "Unexpected broker message type"; + + base::debug::Alias(&buffer[0]); + base::debug::Alias(&bytes_read); + base::debug::Alias(message.get()); + CHECK(false); return nullptr; } diff --git a/chromium/mojo/edk/system/core.cc b/chromium/mojo/edk/system/core.cc index d2a421ebeee..f37d42ef3ec 100644 --- a/chromium/mojo/edk/system/core.cc +++ b/chromium/mojo/edk/system/core.cc @@ -188,16 +188,21 @@ void Core::ChildLaunchFailed(const std::string& child_token) { } ScopedMessagePipeHandle Core::ConnectToPeerProcess( - ScopedPlatformHandle pipe_handle) { + ScopedPlatformHandle pipe_handle, + const std::string& peer_token) { RequestContext request_context; ports::PortRef port0, port1; GetNodeController()->node()->CreatePortPair(&port0, &port1); MojoHandle handle = AddDispatcher(new MessagePipeDispatcher( GetNodeController(), port0, kUnknownPipeIdForDebug, 0)); - GetNodeController()->ConnectToPeer(std::move(pipe_handle), port1); + GetNodeController()->ConnectToPeer(std::move(pipe_handle), port1, peer_token); return ScopedMessagePipeHandle(MessagePipeHandle(handle)); } +void Core::ClosePeerConnection(const std::string& peer_token) { + GetNodeController()->ClosePeerConnection(peer_token); +} + void Core::InitChild(ScopedPlatformHandle platform_handle) { GetNodeController()->ConnectToParent(std::move(platform_handle)); } diff --git a/chromium/mojo/edk/system/core.h b/chromium/mojo/edk/system/core.h index dc635a05608..e32e2d2a8e6 100644 --- a/chromium/mojo/edk/system/core.h +++ b/chromium/mojo/edk/system/core.h @@ -66,8 +66,9 @@ class MOJO_SYSTEM_IMPL_EXPORT Core { // is no common ancestor for the processes involved within this mojo system. // Both processes must call this function, each passing one end of a platform // channel. This returns one end of a message pipe to each process. - ScopedMessagePipeHandle ConnectToPeerProcess( - ScopedPlatformHandle pipe_handle); + ScopedMessagePipeHandle ConnectToPeerProcess(ScopedPlatformHandle pipe_handle, + const std::string& peer_token); + void ClosePeerConnection(const std::string& peer_token); // Called in a child process exactly once during early initialization. void InitChild(ScopedPlatformHandle platform_handle); diff --git a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc b/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc index d5460cb4642..ec8d2d855e4 100644 --- a/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc +++ b/chromium/mojo/edk/system/data_pipe_consumer_dispatcher.cc @@ -527,8 +527,8 @@ void DataPipeConsumerDispatcher::UpdateSignalsStateNoLock() { } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { ports::ScopedMessage message; do { - int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr, - &message); + int rv = node_controller_->node()->GetMessage( + control_port_, &message, nullptr); if (rv != ports::OK) peer_closed_ = true; if (message) { diff --git a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc b/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc index 8273c14d18f..8c1993aa5d3 100644 --- a/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc +++ b/chromium/mojo/edk/system/data_pipe_producer_dispatcher.cc @@ -504,8 +504,8 @@ void DataPipeProducerDispatcher::UpdateSignalsStateNoLock() { } else if (rv == ports::OK && port_status.has_messages && !in_transit_) { ports::ScopedMessage message; do { - int rv = node_controller_->node()->GetMessageIf(control_port_, nullptr, - &message); + int rv = node_controller_->node()->GetMessage( + control_port_, &message, nullptr); if (rv != ports::OK) peer_closed_ = true; if (message) { diff --git a/chromium/mojo/edk/system/message_pipe_dispatcher.cc b/chromium/mojo/edk/system/message_pipe_dispatcher.cc index 999869a172e..f27336b56f4 100644 --- a/chromium/mojo/edk/system/message_pipe_dispatcher.cc +++ b/chromium/mojo/edk/system/message_pipe_dispatcher.cc @@ -14,6 +14,7 @@ #include "mojo/edk/system/core.h" #include "mojo/edk/system/message_for_transit.h" #include "mojo/edk/system/node_controller.h" +#include "mojo/edk/system/ports/message_filter.h" #include "mojo/edk/system/ports_message.h" #include "mojo/edk/system/request_context.h" @@ -59,6 +60,103 @@ class MessagePipeDispatcher::PortObserverThunk DISALLOW_COPY_AND_ASSIGN(PortObserverThunk); }; +// A MessageFilter used by ReadMessage to determine whether a message should +// actually be consumed yet. +class ReadMessageFilter : public ports::MessageFilter { + public: + // Creates a new ReadMessageFilter which captures and potentially modifies + // various (unowned) local state within MessagePipeDispatcher::ReadMessage. + ReadMessageFilter(bool read_any_size, + bool may_discard, + uint32_t* num_bytes, + uint32_t* num_handles, + bool* no_space, + bool* invalid_message) + : read_any_size_(read_any_size), + may_discard_(may_discard), + num_bytes_(num_bytes), + num_handles_(num_handles), + no_space_(no_space), + invalid_message_(invalid_message) {} + + ~ReadMessageFilter() override {} + + // ports::MessageFilter: + bool Match(const ports::Message& m) override { + const PortsMessage& message = static_cast<const PortsMessage&>(m); + if (message.num_payload_bytes() < sizeof(MessageHeader)) { + *invalid_message_ = true; + return true; + } + + const MessageHeader* header = + static_cast<const MessageHeader*>(message.payload_bytes()); + if (header->header_size > message.num_payload_bytes()) { + *invalid_message_ = true; + return true; + } + + uint32_t bytes_to_read = 0; + uint32_t bytes_available = + static_cast<uint32_t>(message.num_payload_bytes()) - + header->header_size; + if (num_bytes_) { + bytes_to_read = std::min(*num_bytes_, bytes_available); + *num_bytes_ = bytes_available; + } + + uint32_t handles_to_read = 0; + uint32_t handles_available = header->num_dispatchers; + if (num_handles_) { + handles_to_read = std::min(*num_handles_, handles_available); + *num_handles_ = handles_available; + } + + if (handles_to_read < handles_available || + (!read_any_size_ && bytes_to_read < bytes_available)) { + *no_space_ = true; + return may_discard_; + } + + return true; + } + + private: + const bool read_any_size_; + const bool may_discard_; + uint32_t* const num_bytes_; + uint32_t* const num_handles_; + bool* const no_space_; + bool* const invalid_message_; + + DISALLOW_COPY_AND_ASSIGN(ReadMessageFilter); +}; + +#if DCHECK_IS_ON() + +// A MessageFilter which never matches a message. Used to peek at the size of +// the next available message on a port, for debug logging only. +class PeekSizeMessageFilter : public ports::MessageFilter { + public: + PeekSizeMessageFilter() {} + ~PeekSizeMessageFilter() override {} + + // ports::MessageFilter: + bool Match(const ports::Message& message) override { + message_size_ = message.num_payload_bytes(); + return false; + } + + size_t message_size() const { return message_size_; } + + private: + size_t message_size_ = 0; + + DISALLOW_COPY_AND_ASSIGN(PeekSizeMessageFilter); +}; + +#endif // DCHECK_IS_ON() + MessagePipeDispatcher::MessagePipeDispatcher(NodeController* node_controller, const ports::PortRef& port, uint64_t pipe_id, @@ -151,8 +249,6 @@ MojoResult MessagePipeDispatcher::WriteMessage( rv == ports::ERROR_PORT_CANNOT_SEND_PEER) { return MOJO_RESULT_INVALID_ARGUMENT; } else if (rv == ports::ERROR_PORT_PEER_CLOSED) { - base::AutoLock lock(signal_lock_); - awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock()); return MOJO_RESULT_FAILED_PRECONDITION; } @@ -186,50 +282,9 @@ MojoResult MessagePipeDispatcher::ReadMessage( // This flag exists to support both new and old API behavior. ports::ScopedMessage ports_message; - int rv = node_controller_->node()->GetMessageIf( - port_, - [read_any_size, num_bytes, num_handles, &no_space, &may_discard, - &invalid_message]( - const ports::Message& next_message) { - const PortsMessage& message = - static_cast<const PortsMessage&>(next_message); - if (message.num_payload_bytes() < sizeof(MessageHeader)) { - invalid_message = true; - return true; - } - - const MessageHeader* header = - static_cast<const MessageHeader*>(message.payload_bytes()); - if (header->header_size > message.num_payload_bytes()) { - invalid_message = true; - return true; - } - - uint32_t bytes_to_read = 0; - uint32_t bytes_available = - static_cast<uint32_t>(message.num_payload_bytes()) - - header->header_size; - if (num_bytes) { - bytes_to_read = std::min(*num_bytes, bytes_available); - *num_bytes = bytes_available; - } - - uint32_t handles_to_read = 0; - uint32_t handles_available = header->num_dispatchers; - if (num_handles) { - handles_to_read = std::min(*num_handles, handles_available); - *num_handles = handles_available; - } - - if (handles_to_read < handles_available || - (!read_any_size && bytes_to_read < bytes_available)) { - no_space = true; - return may_discard; - } - - return true; - }, - &ports_message); + ReadMessageFilter filter(read_any_size, may_discard, num_bytes, num_handles, + &no_space, &invalid_message); + int rv = node_controller_->node()->GetMessage(port_, &ports_message, &filter); if (invalid_message) return MOJO_RESULT_UNKNOWN; @@ -258,8 +313,6 @@ MojoResult MessagePipeDispatcher::ReadMessage( // Peer is closed and there are no more messages to read. DCHECK_EQ(rv, ports::ERROR_PORT_PEER_CLOSED); - base::AutoLock lock(signal_lock_); - awakables_.AwakeForStateChange(GetHandleSignalsStateNoLock()); return MOJO_RESULT_FAILED_PRECONDITION; } @@ -530,15 +583,11 @@ void MessagePipeDispatcher::OnPortStatusChanged() { if (node_controller_->node()->GetStatus(port_, &port_status) == ports::OK) { if (port_status.has_messages) { ports::ScopedMessage unused; - size_t message_size = 0; - node_controller_->node()->GetMessageIf( - port_, [&message_size](const ports::Message& message) { - message_size = message.num_payload_bytes(); - return false; - }, &unused); + PeekSizeMessageFilter filter; + node_controller_->node()->GetMessage(port_, &unused, &filter); DVLOG(4) << "New message detected on message pipe " << pipe_id_ << " endpoint " << endpoint_ << " [port=" << port_.name() - << "; size=" << message_size << "]"; + << "; size=" << filter.message_size() << "]"; } if (port_status.peer_closed) { DVLOG(2) << "Peer closure detected on message pipe " << pipe_id_ diff --git a/chromium/mojo/edk/system/message_pipe_unittest.cc b/chromium/mojo/edk/system/message_pipe_unittest.cc index 8f489508e92..fcfaeca1790 100644 --- a/chromium/mojo/edk/system/message_pipe_unittest.cc +++ b/chromium/mojo/edk/system/message_pipe_unittest.cc @@ -706,17 +706,6 @@ TEST_F(FuseMessagePipeTest, FuseAfterPeerWriteAndClosure) { EXPECT_EQ(MOJO_RESULT_OK, MojoClose(d)); } -TEST_F(MessagePipeTest, ClosePipesStressTest) { - // Stress test to exercise https://crbug.com/665869. - const size_t kNumPipes = 100000; - for (size_t i = 0; i < kNumPipes; ++i) { - MojoHandle a, b; - CreateMessagePipe(&a, &b); - MojoClose(a); - MojoClose(b); - } -} - } // namespace } // namespace edk } // namespace mojo diff --git a/chromium/mojo/edk/system/node_controller.cc b/chromium/mojo/edk/system/node_controller.cc index faab32b75c6..1a481543569 100644 --- a/chromium/mojo/edk/system/node_controller.cc +++ b/chromium/mojo/edk/system/node_controller.cc @@ -229,6 +229,12 @@ void NodeController::CloseChildPorts(const std::string& child_token) { AcceptIncomingMessages(); } +void NodeController::ClosePeerConnection(const std::string& peer_token) { + io_task_runner_->PostTask( + FROM_HERE, base::Bind(&NodeController::ClosePeerConnectionOnIOThread, + base::Unretained(this), peer_token)); +} + void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) { #if !defined(OS_MACOSX) && !defined(OS_NACL_SFI) // Use the bootstrap channel for the broker and receive the node's channel @@ -257,13 +263,14 @@ void NodeController::ConnectToParent(ScopedPlatformHandle platform_handle) { } void NodeController::ConnectToPeer(ScopedPlatformHandle handle, - const ports::PortRef& port) { + const ports::PortRef& port, + const std::string& peer_token) { ports::NodeName node_name; GenerateRandomName(&node_name); io_task_runner_->PostTask( FROM_HERE, base::Bind(&NodeController::ConnectToPeerOnIOThread, base::Unretained(this), base::Passed(&handle), - node_name, port)); + node_name, port, peer_token)); } void NodeController::SetPortObserver( @@ -463,12 +470,15 @@ void NodeController::ConnectToParentOnIOThread( void NodeController::ConnectToPeerOnIOThread(ScopedPlatformHandle handle, ports::NodeName token, - ports::PortRef port) { + ports::PortRef port, + const std::string& peer_token) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); scoped_refptr<NodeChannel> channel = NodeChannel::Create(this, std::move(handle), io_task_runner_, {}); - pending_peers_.insert({token, {channel, port}}); + peer_connections_.insert( + {token, PeerConnection{channel, port, peer_token}}); + peers_by_token_.insert({peer_token, token}); channel->SetRemoteNodeName(token); channel->Start(); @@ -476,6 +486,19 @@ void NodeController::ConnectToPeerOnIOThread(ScopedPlatformHandle handle, channel->AcceptPeer(name_, token, port.name()); } +void NodeController::ClosePeerConnectionOnIOThread( + const std::string& peer_token) { + RequestContext request_context(RequestContext::Source::SYSTEM); + auto peer = peers_by_token_.find(peer_token); + // The connection may already be closed. + if (peer == peers_by_token_.end()) + return; + + // |peer| may be removed so make a copy of |name|. + ports::NodeName name = peer->second; + DropPeer(name, nullptr); +} + scoped_refptr<NodeChannel> NodeController::GetPeerChannel( const ports::NodeName& name) { base::AutoLock lock(peers_lock_); @@ -608,6 +631,13 @@ void NodeController::DropPeer(const ports::NodeName& name, if (is_parent) CancelPendingPortMerges(); + auto peer = peer_connections_.find(name); + if (peer != peer_connections_.end()) { + peers_by_token_.erase(peer->second.peer_token); + ports_to_close.push_back(peer->second.local_port); + peer_connections_.erase(peer); + } + for (const auto& port : ports_to_close) node_->ClosePort(port); @@ -758,7 +788,7 @@ void NodeController::DropAllPeers() { peers_.clear(); pending_children_.clear(); pending_peer_messages_.clear(); - pending_peers_.clear(); + peer_connections_.clear(); } for (const auto& peer : all_peers) @@ -1273,21 +1303,27 @@ void NodeController::OnAcceptPeer(const ports::NodeName& from_node, const ports::PortName& port_name) { DCHECK(io_task_runner_->RunsTasksOnCurrentThread()); - auto it = pending_peers_.find(from_node); - if (it == pending_peers_.end()) { + auto it = peer_connections_.find(from_node); + if (it == peer_connections_.end()) { DLOG(ERROR) << "Received unexpected AcceptPeer message from " << from_node; DropPeer(from_node, nullptr); return; } - scoped_refptr<NodeChannel> channel = it->second.first; - ports::PortRef local_port = it->second.second; - pending_peers_.erase(it); + scoped_refptr<NodeChannel> channel = std::move(it->second.channel); + ports::PortRef local_port = it->second.local_port; + std::string peer_token = std::move(it->second.peer_token); + peer_connections_.erase(it); DCHECK(channel); // If the peer connection is a self connection (which is used in tests), // drop the channel to it and skip straight to merging the ports. - if (name_ != peer_name) { + if (name_ == peer_name) { + peers_by_token_.erase(peer_token); + } else { + peers_by_token_[peer_token] = peer_name; + peer_connections_.insert( + {peer_name, PeerConnection{nullptr, local_port, peer_token}}); DVLOG(1) << "Node " << name_ << " accepted peer " << peer_name; AddPeer(peer_name, channel, false /* start_channel */); @@ -1374,5 +1410,27 @@ void NodeController::AttemptShutdownIfRequested() { callback.Run(); } +NodeController::PeerConnection::PeerConnection() = default; + +NodeController::PeerConnection::PeerConnection( + const PeerConnection& other) = default; + +NodeController::PeerConnection::PeerConnection( + PeerConnection&& other) = default; + +NodeController::PeerConnection::PeerConnection( + const scoped_refptr<NodeChannel>& channel, + const ports::PortRef& local_port, + const std::string& peer_token) + : channel(channel), local_port(local_port), peer_token(peer_token) {} + +NodeController::PeerConnection::~PeerConnection() = default; + +NodeController::PeerConnection& NodeController::PeerConnection:: +operator=(const PeerConnection& other) = default; + +NodeController::PeerConnection& NodeController::PeerConnection:: +operator=(PeerConnection&& other) = default; + } // namespace edk } // namespace mojo diff --git a/chromium/mojo/edk/system/node_controller.h b/chromium/mojo/edk/system/node_controller.h index 8692bd78787..df6883ce615 100644 --- a/chromium/mojo/edk/system/node_controller.h +++ b/chromium/mojo/edk/system/node_controller.h @@ -81,13 +81,18 @@ class NodeController : public ports::NodeDelegate, // |child_token|. void CloseChildPorts(const std::string& child_token); + // Close a connection to a peer associated with |peer_token|. + void ClosePeerConnection(const std::string& peer_token); + // Connects this node to a parent node. The parent node will initiate a // handshake. void ConnectToParent(ScopedPlatformHandle platform_handle); // Connects this node to a peer node. On success, |port| will be merged with // the corresponding port in the peer node. - void ConnectToPeer(ScopedPlatformHandle handle, const ports::PortRef& port); + void ConnectToPeer(ScopedPlatformHandle handle, + const ports::PortRef& port, + const std::string& peer_token); // Sets a port's observer. If |observer| is null the port's current observer // is removed. @@ -143,6 +148,24 @@ class NodeController : public ports::NodeDelegate, const std::string child_token; }; + struct PeerConnection { + PeerConnection(); + PeerConnection(const PeerConnection& other); + PeerConnection(PeerConnection&& other); + PeerConnection(const scoped_refptr<NodeChannel>& channel, + const ports::PortRef& local_port, + const std::string& peer_token); + ~PeerConnection(); + + PeerConnection& operator=(const PeerConnection& other); + PeerConnection& operator=(PeerConnection&& other); + + + scoped_refptr<NodeChannel> channel; + ports::PortRef local_port; + std::string peer_token; + }; + void ConnectToChildOnIOThread( base::ProcessHandle process_handle, ScopedPlatformHandle platform_handle, @@ -152,7 +175,9 @@ class NodeController : public ports::NodeDelegate, void ConnectToPeerOnIOThread(ScopedPlatformHandle handle, ports::NodeName token, - ports::PortRef port); + ports::PortRef port, + const std::string& peer_token); + void ClosePeerConnectionOnIOThread(const std::string& node_name); scoped_refptr<NodeChannel> GetPeerChannel(const ports::NodeName& name); scoped_refptr<NodeChannel> GetParentChannel(); @@ -323,9 +348,11 @@ class NodeController : public ports::NodeDelegate, NodeMap pending_children_; using PeerNodeMap = - std::unordered_map<ports::NodeName, - std::pair<scoped_refptr<NodeChannel>, ports::PortRef>>; - PeerNodeMap pending_peers_; + std::unordered_map<ports::NodeName, PeerConnection>; + PeerNodeMap peer_connections_; + + // Maps from peer token to node name, pending or not. + std::unordered_map<std::string, ports::NodeName> peers_by_token_; // Indicates whether this object should delete itself on IO thread shutdown. // Must only be accessed from the IO thread. diff --git a/chromium/mojo/edk/system/ports/BUILD.gn b/chromium/mojo/edk/system/ports/BUILD.gn index 239b3a4400b..37b25485a9b 100644 --- a/chromium/mojo/edk/system/ports/BUILD.gn +++ b/chromium/mojo/edk/system/ports/BUILD.gn @@ -10,6 +10,7 @@ source_set("ports") { "event.h", "message.cc", "message.h", + "message_filter.h", "message_queue.cc", "message_queue.h", "name.cc", diff --git a/chromium/mojo/edk/system/ports/message_filter.h b/chromium/mojo/edk/system/ports/message_filter.h new file mode 100644 index 00000000000..bf8fa219665 --- /dev/null +++ b/chromium/mojo/edk/system/ports/message_filter.h @@ -0,0 +1,29 @@ +// Copyright 2016 The Chromium 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 MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ +#define MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ + +namespace mojo { +namespace edk { +namespace ports { + +class Message; + +// An interface which can be implemented to filter port messages according to +// arbitrary policy. +class MessageFilter { + public: + virtual ~MessageFilter() {} + + // Returns true of |message| should be accepted by whomever is applying this + // filter. See MessageQueue::GetNextMessage(), for example. + virtual bool Match(const Message& message) = 0; +}; + +} // namespace ports +} // namespace edk +} // namespace mojo + +#endif // MOJO_EDK_SYSTEM_PORTS_MESSAGE_FILTER_H_ diff --git a/chromium/mojo/edk/system/ports/message_queue.cc b/chromium/mojo/edk/system/ports/message_queue.cc index ef2e9400989..defb1b6c759 100644 --- a/chromium/mojo/edk/system/ports/message_queue.cc +++ b/chromium/mojo/edk/system/ports/message_queue.cc @@ -8,6 +8,7 @@ #include "base/logging.h" #include "mojo/edk/system/ports/event.h" +#include "mojo/edk/system/ports/message_filter.h" namespace mojo { namespace edk { @@ -44,10 +45,9 @@ bool MessageQueue::HasNextMessage() const { return !heap_.empty() && GetSequenceNum(heap_[0]) == next_sequence_num_; } -void MessageQueue::GetNextMessageIf( - std::function<bool(const Message&)> selector, - ScopedMessage* message) { - if (!HasNextMessage() || (selector && !selector(*heap_[0].get()))) { +void MessageQueue::GetNextMessage(ScopedMessage* message, + MessageFilter* filter) { + if (!HasNextMessage() || (filter && !filter->Match(*heap_[0].get()))) { message->reset(); return; } diff --git a/chromium/mojo/edk/system/ports/message_queue.h b/chromium/mojo/edk/system/ports/message_queue.h index d90ac1ace9e..d9a47ed0a13 100644 --- a/chromium/mojo/edk/system/ports/message_queue.h +++ b/chromium/mojo/edk/system/ports/message_queue.h @@ -22,6 +22,8 @@ namespace ports { const uint64_t kInitialSequenceNum = 1; const uint64_t kInvalidSequenceNum = std::numeric_limits<uint64_t>::max(); +class MessageFilter; + // An incoming message queue for a port. MessageQueue keeps track of the highest // known sequence number and can indicate whether the next sequential message is // available. Thus the queue enforces message ordering for the consumer without @@ -38,9 +40,9 @@ class MessageQueue { bool HasNextMessage() const; - // Gives ownership of the message. The selector may be null. - void GetNextMessageIf(std::function<bool(const Message&)> selector, - ScopedMessage* message); + // Gives ownership of the message. If |filter| is non-null, the next message + // will only be retrieved if the filter successfully matches it. + void GetNextMessage(ScopedMessage* message, MessageFilter* filter); // Takes ownership of the message. Note: Messages are ordered, so while we // have added a message to the queue, we may still be waiting on a message diff --git a/chromium/mojo/edk/system/ports/node.cc b/chromium/mojo/edk/system/ports/node.cc index d6e2767988c..186e8aaad95 100644 --- a/chromium/mojo/edk/system/ports/node.cc +++ b/chromium/mojo/edk/system/ports/node.cc @@ -8,7 +8,6 @@ #include <utility> -#include "base/atomicops.h" #include "base/logging.h" #include "base/memory/ref_counted.h" #include "base/synchronization/lock.h" @@ -264,16 +263,12 @@ int Node::GetStatus(const PortRef& port_ref, PortStatus* port_status) { return OK; } -int Node::GetMessage(const PortRef& port_ref, ScopedMessage* message) { - return GetMessageIf(port_ref, nullptr, message); -} - -int Node::GetMessageIf(const PortRef& port_ref, - std::function<bool(const Message&)> selector, - ScopedMessage* message) { +int Node::GetMessage(const PortRef& port_ref, + ScopedMessage* message, + MessageFilter* filter) { *message = nullptr; - DVLOG(4) << "GetMessageIf for " << port_ref.name() << "@" << name_; + DVLOG(4) << "GetMessage for " << port_ref.name() << "@" << name_; Port* port = port_ref.port(); { @@ -289,7 +284,7 @@ int Node::GetMessageIf(const PortRef& port_ref, if (!CanAcceptMoreMessages(port)) return ERROR_PORT_PEER_CLOSED; - port->message_queue.GetNextMessageIf(std::move(selector), message); + port->message_queue.GetNextMessage(message, filter); } // Allow referenced ports to trigger PortStatusChanged calls. @@ -809,11 +804,6 @@ scoped_refptr<Port> Node::GetPort_Locked(const PortName& port_name) { if (iter == ports_.end()) return nullptr; -#if defined(OS_ANDROID) && defined(ARCH_CPU_ARM64) - // Workaround for https://crbug.com/665869. - base::subtle::MemoryBarrier(); -#endif - return iter->second; } @@ -1000,7 +990,7 @@ int Node::AcceptPort(const PortName& port_name, << port->last_sequence_num_to_receive << "]"; // A newly accepted port is not signalable until the message referencing the - // new port finds its way to the consumer (see GetMessageIf). + // new port finds its way to the consumer (see GetMessage). port->message_queue.set_signalable(false); int rv = AddPortWithName(port_name, port); @@ -1182,7 +1172,7 @@ int Node::ForwardMessages_Locked(const LockedPort& port, for (;;) { ScopedMessage message; - port->message_queue.GetNextMessageIf(nullptr, &message); + port->message_queue.GetNextMessage(&message, nullptr); if (!message) break; diff --git a/chromium/mojo/edk/system/ports/node.h b/chromium/mojo/edk/system/ports/node.h index 65252a3af71..5a20e1b8aba 100644 --- a/chromium/mojo/edk/system/ports/node.h +++ b/chromium/mojo/edk/system/ports/node.h @@ -44,6 +44,7 @@ struct PortStatus { bool peer_closed; }; +class MessageFilter; class NodeDelegate; class Node { @@ -107,15 +108,15 @@ class Node { // indicate that this port's peer has closed. In such cases GetMessage may // be called until it yields a null message, indicating that no more messages // may be read from the port. - int GetMessage(const PortRef& port_ref, ScopedMessage* message); - - // Like GetMessage, but the caller may optionally supply a selector function - // that decides whether or not to return the message. If |selector| is a - // nullptr, then GetMessageIf acts just like GetMessage. The |selector| may - // not call any Node methods. - int GetMessageIf(const PortRef& port_ref, - std::function<bool(const Message&)> selector, - ScopedMessage* message); + // + // If |filter| is non-null, the next available message is returned only if it + // is matched by the filter. If the provided filter does not match the next + // available message, GetMessage() behaves as if there is no message + // available. Ownership of |filter| is not taken, and it must outlive the + // extent of this call. + int GetMessage(const PortRef& port_ref, + ScopedMessage* message, + MessageFilter* filter); // Sends a message from the specified port to its peer. Note that the message // notification may arrive synchronously (via PortStatusChanged() on the diff --git a/chromium/mojo/edk/system/ports/ports_unittest.cc b/chromium/mojo/edk/system/ports/ports_unittest.cc index b76fa10de5f..cb48b3eb2f4 100644 --- a/chromium/mojo/edk/system/ports/ports_unittest.cc +++ b/chromium/mojo/edk/system/ports/ports_unittest.cc @@ -172,7 +172,7 @@ class TestNode : public NodeDelegate { } bool ReadMessage(const PortRef& port, ScopedMessage* message) { - return node_.GetMessage(port, message) == OK && *message; + return node_.GetMessage(port, message, nullptr) == OK && *message; } bool GetSavedMessage(ScopedMessage* message) { @@ -581,14 +581,15 @@ TEST_F(PortsTest, LostConnectionToNode2) { // a0 should have eventually detected peer closure after node loss. ScopedMessage message; - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node0.node().GetMessage(a0, &message)); + EXPECT_EQ(ERROR_PORT_PEER_CLOSED, + node0.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); EXPECT_EQ(OK, node0.node().ClosePort(a0)); EXPECT_EQ(OK, node0.node().ClosePort(x0)); - EXPECT_EQ(OK, node1.node().GetMessage(x1, &message)); + EXPECT_EQ(OK, node1.node().GetMessage(x1, &message, nullptr)); EXPECT_TRUE(message); node1.ClosePortsInMessage(message.get()); @@ -725,14 +726,15 @@ TEST_F(PortsTest, GetMessage1) { EXPECT_EQ(OK, node.node().CreatePortPair(&a0, &a1)); ScopedMessage message; - EXPECT_EQ(OK, node.node().GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); EXPECT_EQ(OK, node.node().ClosePort(a1)); WaitForIdle(); - EXPECT_EQ(ERROR_PORT_PEER_CLOSED, node.node().GetMessage(a0, &message)); + EXPECT_EQ(ERROR_PORT_PEER_CLOSED, + node.node().GetMessage(a0, &message, nullptr)); EXPECT_FALSE(message); EXPECT_EQ(OK, node.node().ClosePort(a0)); @@ -752,7 +754,7 @@ TEST_F(PortsTest, GetMessage2) { EXPECT_EQ(OK, node.SendStringMessage(a1, "1")); ScopedMessage message; - EXPECT_EQ(OK, node.node().GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); ASSERT_TRUE(message); EXPECT_TRUE(MessageEquals(message, "1")); @@ -781,7 +783,7 @@ TEST_F(PortsTest, GetMessage3) { ScopedMessage message; for (size_t i = 0; i < sizeof(kStrings)/sizeof(kStrings[0]); ++i) { - EXPECT_EQ(OK, node.node().GetMessage(a0, &message)); + EXPECT_EQ(OK, node.node().GetMessage(a0, &message, nullptr)); ASSERT_TRUE(message); EXPECT_TRUE(MessageEquals(message, kStrings[i])); } diff --git a/chromium/mojo/public/BUILD.gn b/chromium/mojo/public/BUILD.gn index 94424796922..9b76102be78 100644 --- a/chromium/mojo/public/BUILD.gn +++ b/chromium/mojo/public/BUILD.gn @@ -13,8 +13,8 @@ group("public") { if (is_android) { deps += [ - "java:bindings", - "java:system", + "java:bindings_java", + "java:system_java", ] } } diff --git a/chromium/mojo/public/README.md b/chromium/mojo/public/README.md index a31a8a8245e..dd91742d781 100644 --- a/chromium/mojo/public/README.md +++ b/chromium/mojo/public/README.md @@ -31,7 +31,7 @@ Platform -------- The platform/ subdirectory contains any build-time requirements (e.g., static -libraries) that may be needed to produce a Mojo application for certain +libraries) that may be needed to produce a Service library for certain platforms, such as a native shared library or as a NaCl binary. Tools diff --git a/chromium/mojo/public/c/README.md b/chromium/mojo/public/c/README.md index 0b022fc1549..223c205e0a3 100644 --- a/chromium/mojo/public/c/README.md +++ b/chromium/mojo/public/c/README.md @@ -7,7 +7,7 @@ System ------ The system/ subdirectory provides definitions of the basic low-level API used by -all Mojo applications (whether directly or indirectly). These consist primarily +all Services (whether directly or indirectly). These consist primarily of the IPC primitives used to communicate with Mojo services. Though the message protocol is stable, the implementation of the transport is diff --git a/chromium/mojo/public/c/system/BUILD.gn b/chromium/mojo/public/c/system/BUILD.gn index cde32282f63..c3b3d5f1bb4 100644 --- a/chromium/mojo/public/c/system/BUILD.gn +++ b/chromium/mojo/public/c/system/BUILD.gn @@ -25,7 +25,7 @@ component("system") { # This should ONLY be depended upon directly by shared_library targets which # need to export the MojoSetSystemThunks symbol, like targets generated by the -# mojo_native_application template in //services/shell/public/cpp/service.gni. +# mojo_native_application template in //services/service_manager/public/cpp/service.gni. source_set("set_thunks_for_app") { sources = [ "set_thunks_for_app.cc", diff --git a/chromium/mojo/public/cpp/bindings/BUILD.gn b/chromium/mojo/public/cpp/bindings/BUILD.gn index 7b60b2c0a5a..2dded0b7d1c 100644 --- a/chromium/mojo/public/cpp/bindings/BUILD.gn +++ b/chromium/mojo/public/cpp/bindings/BUILD.gn @@ -39,7 +39,6 @@ component("bindings") { "bindings_export.h", "connection_error_callback.h", "connector.h", - "enum_traits.h", "filter_chain.h", "interface_data_view.h", "interface_endpoint_client.h", @@ -139,11 +138,13 @@ component("bindings") { "string_traits_stl.h", "string_traits_string16.h", "string_traits_string_piece.h", + "strong_associated_binding.h", "strong_binding.h", "struct_ptr.h", "sync_call_restrictions.h", "sync_handle_registry.h", "sync_handle_watcher.h", + "thread_safe_interface_ptr.h", "type_converter.h", "union_traits.h", ] @@ -166,6 +167,7 @@ component("bindings") { source_set("struct_traits") { sources = [ + "enum_traits.h", "struct_traits.h", ] } diff --git a/chromium/mojo/public/cpp/bindings/array_traits_stl.h b/chromium/mojo/public/cpp/bindings/array_traits_stl.h index c1aac00a7cd..dec47bfe7df 100644 --- a/chromium/mojo/public/cpp/bindings/array_traits_stl.h +++ b/chromium/mojo/public/cpp/bindings/array_traits_stl.h @@ -5,6 +5,8 @@ #ifndef MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ #define MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ +#include <map> +#include <set> #include <vector> #include "mojo/public/cpp/bindings/array_traits.h" @@ -85,6 +87,41 @@ struct ArrayTraits<std::set<T>> { } }; +template <typename K, typename V> +struct MapValuesArrayView { + explicit MapValuesArrayView(const std::map<K, V>& map) : map(map) {} + const std::map<K, V>& map; +}; + +// Convenience function to create a MapValuesArrayView<> that infers the +// template arguments from its argument type. +template <typename K, typename V> +MapValuesArrayView<K, V> MapValuesToArray(const std::map<K, V>& map) { + return MapValuesArrayView<K, V>(map); +} + +// This ArrayTraits specialization is used only for serialization and converts +// a map<K, V> into an array<V>, discarding the keys. +template <typename K, typename V> +struct ArrayTraits<MapValuesArrayView<K, V>> { + using Element = V; + using ConstIterator = typename std::map<K, V>::const_iterator; + + static bool IsNull(const MapValuesArrayView<K, V>& input) { + // std::map<> is always converted to non-null mojom array. + return false; + } + + static size_t GetSize(const MapValuesArrayView<K, V>& input) { + return input.map.size(); + } + static ConstIterator GetBegin(const MapValuesArrayView<K, V>& input) { + return input.map.begin(); + } + static void AdvanceIterator(ConstIterator& iterator) { ++iterator; } + static const V& GetValue(ConstIterator& iterator) { return iterator->second; } +}; + } // namespace mojo #endif // MOJO_PUBLIC_CPP_BINDINGS_ARRAY_TRAITS_STL_H_ diff --git a/chromium/mojo/public/cpp/bindings/associated_binding.h b/chromium/mojo/public/cpp/bindings/associated_binding.h index a7a7c822869..5c3764f5ee6 100644 --- a/chromium/mojo/public/cpp/bindings/associated_binding.h +++ b/chromium/mojo/public/cpp/bindings/associated_binding.h @@ -22,6 +22,7 @@ #include "mojo/public/cpp/bindings/connection_error_callback.h" #include "mojo/public/cpp/bindings/interface_endpoint_client.h" #include "mojo/public/cpp/bindings/lib/control_message_proxy.h" +#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/bindings/scoped_interface_endpoint_handle.h" namespace mojo { @@ -38,37 +39,38 @@ class MessageReceiver; // single thread for the purposes of task scheduling. Please note that incoming // synchrounous method calls may not be run from this task runner, when they // reenter outgoing synchrounous calls on the same thread. -template <typename Interface> +template <typename Interface, + typename ImplRefTraits = RawPtrImplRefTraits<Interface>> class AssociatedBinding { public: + using ImplPointerType = typename ImplRefTraits::PointerType; + // Constructs an incomplete associated binding that will use the // implementation |impl|. It may be completed with a subsequent call to the // |Bind| method. Does not take ownership of |impl|, which must outlive this // object. - explicit AssociatedBinding(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); - } + explicit AssociatedBinding(ImplPointerType impl) { stub_.set_sink(impl); } // Constructs a completed associated binding of |impl|. The output |ptr_info| // should be passed through the message pipe endpoint referred to by // |associated_group| to setup the corresponding asssociated interface // pointer. |impl| must outlive this object. - AssociatedBinding(Interface* impl, + AssociatedBinding(ImplPointerType impl, AssociatedInterfacePtrInfo<Interface>* ptr_info, AssociatedGroup* associated_group, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(impl) { + : AssociatedBinding(std::move(impl)) { Bind(ptr_info, associated_group, std::move(runner)); } // Constructs a completed associated binding of |impl|. |impl| must outlive // the binding. - AssociatedBinding(Interface* impl, + AssociatedBinding(ImplPointerType impl, AssociatedInterfaceRequest<Interface> request, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : AssociatedBinding(impl) { + : AssociatedBinding(std::move(impl)) { Bind(std::move(request), std::move(runner)); } @@ -108,8 +110,10 @@ class AssociatedBinding { base::WrapUnique(new typename Interface::RequestValidator_()), Interface::HasSyncMethods_, std::move(runner), Interface::Version_)); - stub_.serialization_context()->group_controller = - endpoint_client_->group_controller(); + if (Interface::PassesAssociatedKinds_) { + stub_.serialization_context()->group_controller = + endpoint_client_->group_controller(); + } } // Adds a message filter to be notified of each incoming message before @@ -166,7 +170,7 @@ class AssociatedBinding { } // Returns the interface implementation that was previously specified. - Interface* impl() { return impl_; } + Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } // Indicates whether the associated binding has been completed. bool is_bound() const { return !!endpoint_client_; } @@ -187,9 +191,7 @@ class AssociatedBinding { private: std::unique_ptr<InterfaceEndpointClient> endpoint_client_; - - typename Interface::Stub_ stub_; - Interface* impl_; + typename Interface::template Stub_<ImplRefTraits> stub_; DISALLOW_COPY_AND_ASSIGN(AssociatedBinding); }; diff --git a/chromium/mojo/public/cpp/bindings/associated_interface_ptr_info.h b/chromium/mojo/public/cpp/bindings/associated_interface_ptr_info.h index bfb3297a0f8..3c6ca546031 100644 --- a/chromium/mojo/public/cpp/bindings/associated_interface_ptr_info.h +++ b/chromium/mojo/public/cpp/bindings/associated_interface_ptr_info.h @@ -20,6 +20,7 @@ template <typename Interface> class AssociatedInterfacePtrInfo { public: AssociatedInterfacePtrInfo() : version_(0u) {} + AssociatedInterfacePtrInfo(std::nullptr_t) : version_(0u) {} AssociatedInterfacePtrInfo(AssociatedInterfacePtrInfo&& other) : handle_(std::move(other.handle_)), version_(other.version_) { diff --git a/chromium/mojo/public/cpp/bindings/binding.h b/chromium/mojo/public/cpp/bindings/binding.h index 71257d2ac42..249655d6e05 100644 --- a/chromium/mojo/public/cpp/bindings/binding.h +++ b/chromium/mojo/public/cpp/bindings/binding.h @@ -18,6 +18,7 @@ #include "mojo/public/cpp/bindings/interface_ptr_info.h" #include "mojo/public/cpp/bindings/interface_request.h" #include "mojo/public/cpp/bindings/lib/binding_state.h" +#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/system/core.h" namespace mojo { @@ -66,21 +67,24 @@ class MessageReceiver; // single thread for the purposes of task scheduling. Please note that incoming // synchrounous method calls may not be run from this task runner, when they // reenter outgoing synchrounous calls on the same thread. -template <typename Interface> +template <typename Interface, + typename ImplRefTraits = RawPtrImplRefTraits<Interface>> class Binding { public: + using ImplPointerType = typename ImplRefTraits::PointerType; + // Constructs an incomplete binding that will use the implementation |impl|. // The binding may be completed with a subsequent call to the |Bind| method. // Does not take ownership of |impl|, which must outlive the binding. - explicit Binding(Interface* impl) : internal_state_(impl) {} + explicit Binding(ImplPointerType impl) : internal_state_(std::move(impl)) {} // Constructs a completed binding of message pipe |handle| to implementation // |impl|. Does not take ownership of |impl|, which must outlive the binding. - Binding(Interface* impl, + Binding(ImplPointerType impl, ScopedMessagePipeHandle handle, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(impl)) { Bind(std::move(handle), std::move(runner)); } @@ -89,22 +93,22 @@ class Binding { // pass |ptr| on to the client of the service. Does not take ownership of any // of the parameters. |impl| must outlive the binding. |ptr| only needs to // last until the constructor returns. - Binding(Interface* impl, + Binding(ImplPointerType impl, InterfacePtr<Interface>* ptr, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(impl)) { Bind(ptr, std::move(runner)); } // Constructs a completed binding of |impl| to the message pipe endpoint in // |request|, taking ownership of the endpoint. Does not take ownership of // |impl|, which must outlive the binding. - Binding(Interface* impl, + Binding(ImplPointerType impl, InterfaceRequest<Interface> request, scoped_refptr<base::SingleThreadTaskRunner> runner = base::ThreadTaskRunnerHandle::Get()) - : Binding(impl) { + : Binding(std::move(impl)) { Bind(request.PassMessagePipe(), std::move(runner)); } @@ -271,7 +275,7 @@ class Binding { void EnableTestingMode() { internal_state_.EnableTestingMode(); } private: - internal::BindingState<Interface, true> internal_state_; + internal::BindingState<Interface, true, ImplRefTraits> internal_state_; DISALLOW_COPY_AND_ASSIGN(Binding); }; diff --git a/chromium/mojo/public/cpp/bindings/binding_set.h b/chromium/mojo/public/cpp/bindings/binding_set.h index d778f6c3478..0ae23889849 100644 --- a/chromium/mojo/public/cpp/bindings/binding_set.h +++ b/chromium/mojo/public/cpp/bindings/binding_set.h @@ -137,9 +137,8 @@ class BindingSet { } void FlushForTesting() { - for (auto& binding : bindings_) { - binding.first->FlushForTesting(); - } + for (auto& binding : bindings_) + binding.second->FlushForTesting(); } private: diff --git a/chromium/mojo/public/cpp/bindings/interface_ptr_set.h b/chromium/mojo/public/cpp/bindings/interface_ptr_set.h index d4b2046f5b1..09a268229dd 100644 --- a/chromium/mojo/public/cpp/bindings/interface_ptr_set.h +++ b/chromium/mojo/public/cpp/bindings/interface_ptr_set.h @@ -16,6 +16,10 @@ namespace mojo { namespace internal { +// TODO(blundell): This class should be rewritten to be structured +// similarly to BindingSet if possible, with PtrSet owning its +// Elements and those Elements calling back into PtrSet on connection +// error. template <typename Interface, template <typename> class Ptr> class PtrSet { public: @@ -55,7 +59,13 @@ class PtrSet { ~Element() {} - void Close() { ptr_.reset(); } + void Close() { + ptr_.reset(); + + // Resetting the interface ptr means that it won't call this object back + // on connection error anymore, so this object must delete itself now. + DeleteElement(this); + } Interface* get() { return ptr_.get(); } diff --git a/chromium/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h b/chromium/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h index 839978dbbaa..c2d40a95c58 100644 --- a/chromium/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/associated_interface_ptr_state.h @@ -102,8 +102,9 @@ class AssociatedInterfacePtrState { base::WrapUnique(new typename Interface::ResponseValidator_()), false, std::move(runner), 0u)); proxy_.reset(new Proxy(endpoint_client_.get())); - proxy_->serialization_context()->group_controller = - endpoint_client_->group_controller(); + if (Interface::PassesAssociatedKinds_) { + proxy_->set_group_controller(endpoint_client_->group_controller()); + } } // After this method is called, the object is in an invalid state and @@ -141,6 +142,13 @@ class AssociatedInterfacePtrState { return endpoint_client_ ? endpoint_client_->associated_group() : nullptr; } + void ForwardMessage(Message message) { endpoint_client_->Accept(&message); } + + void ForwardMessageWithResponder(Message message, + std::unique_ptr<MessageReceiver> responder) { + endpoint_client_->AcceptWithResponder(&message, responder.release()); + } + private: using Proxy = typename Interface::Proxy_; diff --git a/chromium/mojo/public/cpp/bindings/lib/binding_state.h b/chromium/mojo/public/cpp/bindings/lib/binding_state.h index 98eccb7186b..a947cf73c57 100644 --- a/chromium/mojo/public/cpp/bindings/lib/binding_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/binding_state.h @@ -92,18 +92,21 @@ class MOJO_CPP_BINDINGS_EXPORT SimpleBindingState { internal::Router* router_ = nullptr; }; -template <typename Interface, bool use_multiplex_router> +template <typename Interface, bool use_multiplex_router, typename ImplRefTraits> class BindingState; // Uses a single-threaded, dedicated router. If |Interface| doesn't have any // methods to pass associated interface pointers or requests, there won't be // multiple interfaces running on the underlying message pipe. In that case, we // can use this specialization to reduce cost. -template <typename Interface> -class BindingState<Interface, false> : public SimpleBindingState { +template <typename Interface, typename ImplRefTraits> +class BindingState<Interface, false, ImplRefTraits> + : public SimpleBindingState { public: - explicit BindingState(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); + using ImplPointerType = typename ImplRefTraits::PointerType; + + explicit BindingState(ImplPointerType impl) { + stub_.set_sink(std::move(impl)); } ~BindingState() { Close(); } @@ -124,11 +127,10 @@ class BindingState<Interface, false> : public SimpleBindingState { return std::move(request); } - Interface* impl() { return impl_; } + Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } private: - typename Interface::Stub_ stub_; - Interface* impl_; + typename Interface::template Stub_<ImplRefTraits> stub_; DISALLOW_COPY_AND_ASSIGN(BindingState); }; @@ -195,11 +197,14 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexedBindingState { // Uses a multiplexing router. If |Interface| has methods to pass associated // interface pointers or requests, this specialization should be used. -template <typename Interface> -class BindingState<Interface, true> : public MultiplexedBindingState { +template <typename Interface, typename ImplRefTraits> +class BindingState<Interface, true, ImplRefTraits> + : public MultiplexedBindingState { public: - explicit BindingState(Interface* impl) : impl_(impl) { - stub_.set_sink(impl_); + using ImplPointerType = typename ImplRefTraits::PointerType; + + explicit BindingState(ImplPointerType impl) { + stub_.set_sink(std::move(impl)); } ~BindingState() { Close(); } @@ -211,7 +216,8 @@ class BindingState<Interface, true> : public MultiplexedBindingState { base::MakeUnique<typename Interface::RequestValidator_>(), Interface::PassesAssociatedKinds_, Interface::HasSyncMethods_, &stub_, Interface::Version_); - stub_.serialization_context()->group_controller = router_; + if (Interface::PassesAssociatedKinds_) + stub_.serialization_context()->group_controller = router_; } InterfaceRequest<Interface> Unbind() { @@ -222,11 +228,10 @@ class BindingState<Interface, true> : public MultiplexedBindingState { return request; } - Interface* impl() { return impl_; } + Interface* impl() { return ImplRefTraits::GetRawPointer(&stub_.sink()); } private: - typename Interface::Stub_ stub_; - Interface* impl_; + typename Interface::template Stub_<ImplRefTraits> stub_; DISALLOW_COPY_AND_ASSIGN(BindingState); }; diff --git a/chromium/mojo/public/cpp/bindings/lib/handle_interface_serialization.h b/chromium/mojo/public/cpp/bindings/lib/handle_interface_serialization.h index ecfb5bb5fed..8b26d84b64b 100644 --- a/chromium/mojo/public/cpp/bindings/lib/handle_interface_serialization.h +++ b/chromium/mojo/public/cpp/bindings/lib/handle_interface_serialization.h @@ -30,8 +30,10 @@ struct Serializer<AssociatedInterfacePtrInfoDataView<Base>, AssociatedInterface_Data* output, SerializationContext* context) { DCHECK(!input.handle().is_valid() || !input.handle().is_local()); - DCHECK_EQ(input.handle().group_controller(), - context->group_controller.get()); + if (input.handle().is_valid()) { + DCHECK_EQ(input.handle().group_controller(), + context->group_controller.get()); + } output->version = input.version(); output->interface_id = input.PassHandle().release(); } @@ -55,8 +57,10 @@ struct Serializer<AssociatedInterfaceRequestDataView<Base>, AssociatedInterfaceRequest_Data* output, SerializationContext* context) { DCHECK(!input.handle().is_valid() || !input.handle().is_local()); - DCHECK_EQ(input.handle().group_controller(), - context->group_controller.get()); + if (input.handle().is_valid()) { + DCHECK_EQ(input.handle().group_controller(), + context->group_controller.get()); + } output->interface_id = input.PassHandle().release(); } diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc index df16ecfaeea..a6a9c0686bb 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc +++ b/chromium/mojo/public/cpp/bindings/lib/interface_endpoint_client.cc @@ -46,7 +46,7 @@ class ResponderThunk : public MessageReceiverWithStatus { task_runner_(std::move(runner)) {} ~ResponderThunk() override { if (!accept_was_invoked_) { - // The Mojo application handled a message that was expecting a response + // The Service handled a message that was expecting a response // but did not send a response. // We raise an error to signal the calling application that an error // condition occurred. Without this the calling application would have no @@ -280,9 +280,11 @@ void InterfaceEndpointClient::NotifyError() { return; encountered_error_ = true; - // The callbacks may hold on to resources. There is no need to keep them any - // longer. - async_responders_.clear(); + // Response callbacks may hold on to resource, and there's no need to keep + // them alive any longer. Note that it's allowed that a pending response + // callback may own this endpoint, so we simply move the responders onto the + // stack here and let them be destroyed when the stack unwinds. + AsyncResponderMap responders = std::move(async_responders_); control_message_proxy_.OnConnectionError(); diff --git a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h index 78aa02e1763..8f73a25d109 100644 --- a/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h +++ b/chromium/mojo/public/cpp/bindings/lib/interface_ptr_state.h @@ -331,6 +331,17 @@ class InterfacePtrState<Interface, true> { router_->EnableTestingMode(); } + void ForwardMessage(Message message) { + ConfigureProxyIfNecessary(); + endpoint_client_->Accept(&message); + } + + void ForwardMessageWithResponder(Message message, + std::unique_ptr<MessageReceiver> responder) { + ConfigureProxyIfNecessary(); + endpoint_client_->AcceptWithResponder(&message, responder.release()); + } + private: using Proxy = typename Interface::Proxy_; @@ -361,8 +372,8 @@ class InterfacePtrState<Interface, true> { // will not be used. 0u)); proxy_.reset(new Proxy(endpoint_client_.get())); - proxy_->serialization_context()->group_controller = - endpoint_client_->group_controller(); + if (Interface::PassesAssociatedKinds_) + proxy_->set_group_controller(endpoint_client_->group_controller()); } void OnQueryVersion(const base::Callback<void(uint32_t)>& callback, diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc index ef0426fa976..5602b2a0591 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.cc @@ -402,6 +402,7 @@ void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) { DCHECK(!IsMasterInterfaceId(id)); // We will receive a NotifyPeerEndpointClosed message from the other side. + MayAutoUnlock unlocker(lock_.get()); control_message_proxy_.NotifyEndpointClosedBeforeSent(id); return; @@ -413,8 +414,10 @@ void MultiplexRouter::CloseEndpointHandle(InterfaceId id, bool is_local) { DCHECK(!endpoint->closed()); UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); - if (!IsMasterInterfaceId(id)) + if (!IsMasterInterfaceId(id)) { + MayAutoUnlock unlocker(lock_.get()); control_message_proxy_.NotifyPeerEndpointClosed(id); + } ProcessTasks(NO_DIRECT_CLIENT_CALLS, nullptr); } @@ -596,6 +599,7 @@ bool MultiplexRouter::OnAssociatedEndpointClosedBeforeSent(InterfaceId id) { DCHECK(!endpoint->closed()); UpdateEndpointStateMayRemove(endpoint, ENDPOINT_CLOSED); + MayAutoUnlock unlocker(lock_.get()); control_message_proxy_.NotifyPeerEndpointClosed(id); return true; @@ -781,6 +785,7 @@ bool MultiplexRouter::ProcessIncomingMessage( // registration. We continue to process remaining tasks in the queue, as // long as there are refs keeping the router alive. If there are remaining // messages for the master endpoint, we will get here. + MayAutoUnlock unlocker(lock_.get()); if (!IsMasterInterfaceId(id)) control_message_proxy_.NotifyPeerEndpointClosed(id); return true; diff --git a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h index 6ecae85b6ef..8a6e2ed28b0 100644 --- a/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h +++ b/chromium/mojo/public/cpp/bindings/lib/multiplex_router.h @@ -242,6 +242,8 @@ class MOJO_CPP_BINDINGS_EXPORT MultiplexRouter // Sets to nullptr in Config::SINGLE_INTERFACE* mode. std::unique_ptr<base::Lock> lock_; PipeControlMessageHandler control_message_handler_; + + // NOTE: It is unsafe to call into this object while holding |lock_|. PipeControlMessageProxy control_message_proxy_; std::map<InterfaceId, scoped_refptr<InterfaceEndpoint>> endpoints_; diff --git a/chromium/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc b/chromium/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc index c1508ae3374..e2155e79356 100644 --- a/chromium/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc +++ b/chromium/mojo/public/cpp/bindings/lib/pipe_control_message_proxy.cc @@ -54,7 +54,8 @@ void PipeControlMessageProxy::NotifyPeerEndpointClosed(InterfaceId id) { pipe_control::RunOrClosePipeInput::New()); input->set_peer_associated_endpoint_closed_event(std::move(event)); - SendRunOrClosePipeMessage(receiver_, std::move(input), &context_); + internal::SerializationContext context; + SendRunOrClosePipeMessage(receiver_, std::move(input), &context); } void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) { @@ -67,7 +68,8 @@ void PipeControlMessageProxy::NotifyEndpointClosedBeforeSent(InterfaceId id) { pipe_control::RunOrClosePipeInput::New()); input->set_associated_endpoint_closed_before_sent_event(std::move(event)); - SendRunOrClosePipeMessage(receiver_, std::move(input), &context_); + internal::SerializationContext context; + SendRunOrClosePipeMessage(receiver_, std::move(input), &context); } } // namespace mojo diff --git a/chromium/mojo/public/cpp/bindings/lib/router.cc b/chromium/mojo/public/cpp/bindings/lib/router.cc index 8db5c1bbe4c..36f6625041a 100644 --- a/chromium/mojo/public/cpp/bindings/lib/router.cc +++ b/chromium/mojo/public/cpp/bindings/lib/router.cc @@ -38,7 +38,7 @@ class ResponderThunk : public MessageReceiverWithStatus { task_runner_(std::move(runner)) {} ~ResponderThunk() override { if (!accept_was_invoked_) { - // The Mojo application handled a message that was expecting a response + // The Service handled a message that was expecting a response // but did not send a response. // We raise an error to signal the calling application that an error // condition occurred. Without this the calling application would have no @@ -323,9 +323,11 @@ void Router::OnConnectionError() { encountered_error_ = true; - // The callbacks may hold on to resources. There is no need to keep them any - // longer. - async_responders_.clear(); + // Response callbacks may hold on to resource, and there's no need to keep + // them alive any longer. Note that it's allowed that a pending response + // callback may own this endpoint, so we simply move the responders onto the + // stack here and let them be destroyed when the stack unwinds. + AsyncResponderMap responders = std::move(async_responders_); if (!error_handler_.is_null()) { error_handler_.Run(); diff --git a/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc b/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc index 67106a0fd50..12e6ff73c82 100644 --- a/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc +++ b/chromium/mojo/public/cpp/bindings/lib/validation_errors.cc @@ -14,6 +14,7 @@ namespace { ValidationErrorObserverForTesting* g_validation_error_observer = nullptr; SerializationWarningObserverForTesting* g_serialization_warning_observer = nullptr; +bool g_suppress_logging = false; } // namespace @@ -71,8 +72,10 @@ void ReportValidationError(ValidationContext* context, } if (description) { - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) << " (" - << description << ")"; + if (!g_suppress_logging) { + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error) + << " (" << description << ")"; + } if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s (%s)]", @@ -80,7 +83,8 @@ void ReportValidationError(ValidationContext* context, ValidationErrorToString(error), description)); } } else { - LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); + if (!g_suppress_logging) + LOG(ERROR) << "Invalid message: " << ValidationErrorToString(error); if (context->message()) { context->message()->NotifyBadMessage( base::StringPrintf("Validation failed for %s [%s]", @@ -101,6 +105,17 @@ void ReportValidationErrorForMessage( ReportValidationError(&validation_context, error); } +ScopedSuppressValidationErrorLoggingForTests + ::ScopedSuppressValidationErrorLoggingForTests() + : was_suppressed_(g_suppress_logging) { + g_suppress_logging = true; +} + +ScopedSuppressValidationErrorLoggingForTests + ::~ScopedSuppressValidationErrorLoggingForTests() { + g_suppress_logging = was_suppressed_; +} + ValidationErrorObserverForTesting::ValidationErrorObserverForTesting( const base::Closure& callback) : last_error_(VALIDATION_ERROR_NONE), callback_(callback) { diff --git a/chromium/mojo/public/cpp/bindings/lib/validation_errors.h b/chromium/mojo/public/cpp/bindings/lib/validation_errors.h index 7636e391dad..122418d9e3d 100644 --- a/chromium/mojo/public/cpp/bindings/lib/validation_errors.h +++ b/chromium/mojo/public/cpp/bindings/lib/validation_errors.h @@ -89,6 +89,20 @@ MOJO_CPP_BINDINGS_EXPORT void ReportValidationErrorForMessage( ValidationError error, const char* description = nullptr); +// This class may be used by tests to suppress validation error logging. This is +// not thread-safe and must only be instantiated on the main thread with no +// other threads using Mojo bindings at the time of construction or destruction. +class MOJO_CPP_BINDINGS_EXPORT ScopedSuppressValidationErrorLoggingForTests { + public: + ScopedSuppressValidationErrorLoggingForTests(); + ~ScopedSuppressValidationErrorLoggingForTests(); + + private: + const bool was_suppressed_; + + DISALLOW_COPY_AND_ASSIGN(ScopedSuppressValidationErrorLoggingForTests); +}; + // Only used by validation tests and when there is only one thread doing message // validation. class MOJO_CPP_BINDINGS_EXPORT ValidationErrorObserverForTesting { diff --git a/chromium/mojo/public/cpp/bindings/pipe_control_message_proxy.h b/chromium/mojo/public/cpp/bindings/pipe_control_message_proxy.h index 25ee2038469..f5da26b7493 100644 --- a/chromium/mojo/public/cpp/bindings/pipe_control_message_proxy.h +++ b/chromium/mojo/public/cpp/bindings/pipe_control_message_proxy.h @@ -15,9 +15,12 @@ namespace mojo { class MessageReceiver; // Proxy for request messages defined in pipe_control_messages.mojom. +// +// NOTE: This object may be used from multiple threads. class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy { public: - // Doesn't take ownership of |receiver|. It must outlive this object. + // Doesn't take ownership of |receiver|. If This PipeControlMessageProxy will + // be used from multiple threads, |receiver| must be thread-safe. explicit PipeControlMessageProxy(MessageReceiver* receiver); void NotifyPeerEndpointClosed(InterfaceId id); @@ -26,7 +29,6 @@ class MOJO_CPP_BINDINGS_EXPORT PipeControlMessageProxy { private: // Not owned. MessageReceiver* receiver_; - internal::SerializationContext context_; DISALLOW_COPY_AND_ASSIGN(PipeControlMessageProxy); }; diff --git a/chromium/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h b/chromium/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h new file mode 100644 index 00000000000..4d40cdfc12d --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h @@ -0,0 +1,22 @@ +// Copyright 2016 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_RAW_PTR_IMPL_REF_TRAITS_H_ + +namespace mojo { + +// Default traits for a binding's implementation reference type. This +// corresponds to a raw pointer. +template <typename Interface> +struct RawPtrImplRefTraits { + using PointerType = Interface*; + + static bool IsNull(PointerType ptr) { return !ptr; } + static Interface* GetRawPointer(PointerType* ptr) { return *ptr; } +}; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_LIB_RAW_PTR_IMPL_REF_TRAITS_H_ diff --git a/chromium/mojo/public/cpp/bindings/strong_associated_binding.h b/chromium/mojo/public/cpp/bindings/strong_associated_binding.h new file mode 100644 index 00000000000..4d77a35ee8e --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/strong_associated_binding.h @@ -0,0 +1,122 @@ +// Copyright 2016 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_STRONG_ASSOCIATED_BINDING_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_STRONG_ASSOCIATED_BINDING_H_ + +#include <memory> +#include <string> +#include <utility> + +#include "base/bind.h" +#include "base/callback.h" +#include "base/logging.h" +#include "base/macros.h" +#include "base/memory/weak_ptr.h" +#include "mojo/public/cpp/bindings/associated_binding.h" +#include "mojo/public/cpp/bindings/associated_interface_request.h" +#include "mojo/public/cpp/bindings/connection_error_callback.h" +#include "mojo/public/cpp/system/core.h" + +namespace mojo { + +template <typename Interface> +class StrongAssociatedBinding; + +template <typename Interface> +using StrongAssociatedBindingPtr = + base::WeakPtr<StrongAssociatedBinding<Interface>>; + +// This connects an interface implementation strongly to an associated pipe. +// When a connection error is detected the implementation is deleted. +// +// To use, call StrongAssociatedBinding<T>::Create() (see below) or the helper +// MakeStrongAssociatedBinding function: +// +// mojo::MakeStrongAssociatedBinding(base::MakeUnique<FooImpl>(), +// std::move(foo_request)); +// +template <typename Interface> +class StrongAssociatedBinding { + public: + // Create a new StrongAssociatedBinding instance. The instance owns itself, + // cleaning up only in the event of a pipe connection error. Returns a WeakPtr + // to the new StrongAssociatedBinding instance. + static StrongAssociatedBindingPtr<Interface> Create( + std::unique_ptr<Interface> impl, + AssociatedInterfaceRequest<Interface> request) { + StrongAssociatedBinding* binding = + new StrongAssociatedBinding(std::move(impl), std::move(request)); + return binding->weak_factory_.GetWeakPtr(); + } + + // Note: The error handler must not delete the interface implementation. + // + // This method may only be called after this StrongAssociatedBinding has been + // bound to a message pipe. + void set_connection_error_handler(const base::Closure& error_handler) { + DCHECK(binding_.is_bound()); + connection_error_handler_ = error_handler; + connection_error_with_reason_handler_.Reset(); + } + + void set_connection_error_with_reason_handler( + const ConnectionErrorWithReasonCallback& error_handler) { + DCHECK(binding_.is_bound()); + connection_error_with_reason_handler_ = error_handler; + connection_error_handler_.Reset(); + } + + // Forces the binding to close. This destroys the StrongBinding instance. + void Close() { delete this; } + + Interface* impl() { return impl_.get(); } + + // Sends a message on the underlying message pipe and runs the current + // message loop until its response is received. This can be used in tests to + // verify that no message was sent on a message pipe in response to some + // stimulus. + void FlushForTesting() { binding_.FlushForTesting(); } + + private: + StrongAssociatedBinding(std::unique_ptr<Interface> impl, + AssociatedInterfaceRequest<Interface> request) + : impl_(std::move(impl)), + binding_(impl_.get(), std::move(request)), + weak_factory_(this) { + binding_.set_connection_error_with_reason_handler(base::Bind( + &StrongAssociatedBinding::OnConnectionError, base::Unretained(this))); + } + + ~StrongAssociatedBinding() {} + + void OnConnectionError(uint32_t custom_reason, + const std::string& description) { + if (!connection_error_handler_.is_null()) + connection_error_handler_.Run(); + else if (!connection_error_with_reason_handler_.is_null()) + connection_error_with_reason_handler_.Run(custom_reason, description); + Close(); + } + + std::unique_ptr<Interface> impl_; + base::Closure connection_error_handler_; + ConnectionErrorWithReasonCallback connection_error_with_reason_handler_; + AssociatedBinding<Interface> binding_; + base::WeakPtrFactory<StrongAssociatedBinding> weak_factory_; + + DISALLOW_COPY_AND_ASSIGN(StrongAssociatedBinding); +}; + +template <typename Interface, typename Impl> +StrongAssociatedBindingPtr<Interface> MakeStrongAssociatedBinding( + std::unique_ptr<Impl> impl, + AssociatedInterfaceRequest<Interface> request) { + return StrongAssociatedBinding<Interface>::Create(std::move(impl), + std::move(request)); +} + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_STRONG_ASSOCIATED_BINDING_H_ diff --git a/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h b/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h index dccd7309519..1552ce79c93 100644 --- a/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h +++ b/chromium/mojo/public/cpp/bindings/sync_call_restrictions.h @@ -19,6 +19,10 @@ namespace ui { class GpuService; } +namespace aura { +class GpuService; +} + namespace views { class ClipboardMus; } @@ -52,6 +56,7 @@ class MOJO_CPP_BINDINGS_EXPORT SyncCallRestrictions { // DO NOT ADD ANY OTHER FRIEND STATEMENTS, talk to mojo/OWNERS first. // BEGIN ALLOWED USAGE. friend class ui::GpuService; // http://crbug.com/620058 + friend class aura::GpuService; // http://crbug.com/620058 // END ALLOWED USAGE. // BEGIN USAGE THAT NEEDS TO BE FIXED. diff --git a/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h b/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h new file mode 100644 index 00000000000..8f0e139ff97 --- /dev/null +++ b/chromium/mojo/public/cpp/bindings/thread_safe_interface_ptr.h @@ -0,0 +1,175 @@ +// Copyright 2016 The Chromium 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 MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_BASE_H_ +#define MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_BASE_H_ + +#include <memory> + +#include "base/macros.h" +#include "base/memory/ref_counted.h" +#include "base/task_runner.h" +#include "base/threading/thread_task_runner_handle.h" +#include "mojo/public/cpp/bindings/associated_interface_ptr.h" +#include "mojo/public/cpp/bindings/interface_ptr.h" +#include "mojo/public/cpp/bindings/message.h" + +namespace mojo { + +struct ThreadSafeInterfacePtrDeleter; + +// ThreadSafeInterfacePtr and ThreadSafeAssociatedInterfacePtr are versions of +// InterfacePtr and AssociatedInterfacePtr that let caller invoke +// interface methods from any threads. Callbacks are received on the thread that +// performed the interface call. +// +// To create a ThreadSafeInterfacePtr/ThreadSafeAssociatedInterfacePtr, first +// create a regular InterfacePtr/AssociatedInterfacePtr that +// you then provide to ThreadSafeInterfacePtr/AssociatedInterfacePtr::Create. +// You can then call methods on the +// ThreadSafeInterfacePtr/AssociatedInterfacePtr instance from any thread. +// +// Ex for ThreadSafeInterfacePtr: +// frob::FrobinatorPtr frobinator; +// frob::FrobinatorImpl impl(GetProxy(&frobinator)); +// scoped_refptr<frob::ThreadSafeFrobinatorPtr> thread_safe_frobinator = +// frob::ThreadSafeFrobinatorPtr::Create(std::move(frobinator)); +// (*thread_safe_frobinator)->FrobinateToTheMax(); + +template <typename Interface, template <typename> class InterfacePtrType> +class ThreadSafeInterfacePtrBase + : public MessageReceiverWithResponder, + public base::RefCountedThreadSafe< + ThreadSafeInterfacePtrBase<Interface, InterfacePtrType>, + ThreadSafeInterfacePtrDeleter> { + public: + using ProxyType = typename Interface::Proxy_; + + static scoped_refptr<ThreadSafeInterfacePtrBase<Interface, InterfacePtrType>> + Create(InterfacePtrType<Interface> interface_ptr) { + if (!interface_ptr.is_bound()) { + LOG(ERROR) << "Attempting to create a ThreadSafe[Associated]InterfacePtr " + "from an unbound InterfacePtr."; + return nullptr; + } + return new ThreadSafeInterfacePtrBase(std::move(interface_ptr), + base::ThreadTaskRunnerHandle::Get()); + } + + ~ThreadSafeInterfacePtrBase() override {} + + Interface* get() { return &proxy_; } + Interface* operator->() { return get(); } + Interface& operator*() { return *get(); } + + protected: + ThreadSafeInterfacePtrBase( + InterfacePtrType<Interface> interface_ptr, + scoped_refptr<base::SingleThreadTaskRunner> task_runner) + : interface_ptr_task_runner_(task_runner), + proxy_(this), + interface_ptr_(std::move(interface_ptr)), + weak_ptr_factory_(this) {} + + private: + friend class base::RefCountedThreadSafe< + ThreadSafeInterfacePtrBase<Interface, InterfacePtrType>>; + friend struct ThreadSafeInterfacePtrDeleter; + + void DeleteOnCorrectThread() const { + if (!interface_ptr_task_runner_->BelongsToCurrentThread() && + interface_ptr_task_runner_->DeleteSoon(FROM_HERE, this)) { + return; + } + delete this; + } + + // MessageReceiverWithResponder implementation: + bool Accept(Message* message) override { + interface_ptr_task_runner_->PostTask( + FROM_HERE, + base::Bind(&ThreadSafeInterfacePtrBase::AcceptOnInterfacePtrThread, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(*message)))); + return true; + } + + bool AcceptWithResponder(Message* message, + MessageReceiver* responder) override { + auto forward_responder = base::MakeUnique<ForwardToCallingThread>( + base::WrapUnique(responder)); + interface_ptr_task_runner_->PostTask( + FROM_HERE, base::Bind(&ThreadSafeInterfacePtrBase:: + AcceptWithResponderOnInterfacePtrThread, + weak_ptr_factory_.GetWeakPtr(), + base::Passed(std::move(*message)), + base::Passed(std::move(forward_responder)))); + return true; + } + + void AcceptOnInterfacePtrThread(Message message) { + interface_ptr_.internal_state()->ForwardMessage(std::move(message)); + } + void AcceptWithResponderOnInterfacePtrThread( + Message message, + std::unique_ptr<MessageReceiver> responder) { + interface_ptr_.internal_state()->ForwardMessageWithResponder( + std::move(message), std::move(responder)); + } + + class ForwardToCallingThread : public MessageReceiver { + public: + explicit ForwardToCallingThread(std::unique_ptr<MessageReceiver> responder) + : responder_(std::move(responder)), + caller_task_runner_(base::ThreadTaskRunnerHandle::Get()) { + } + + private: + bool Accept(Message* message) { + // The current instance will be deleted when this method returns, so we + // have to relinquish the responder's ownership so it does not get + // deleted. + caller_task_runner_->PostTask(FROM_HERE, + base::Bind(&ForwardToCallingThread::CallAcceptAndDeleteResponder, + base::Passed(std::move(responder_)), + base::Passed(std::move(*message)))); + return true; + } + + static void CallAcceptAndDeleteResponder( + std::unique_ptr<MessageReceiver> responder, + Message message) { + ignore_result(responder->Accept(&message)); + } + + std::unique_ptr<MessageReceiver> responder_; + scoped_refptr<base::SingleThreadTaskRunner> caller_task_runner_; + }; + + scoped_refptr<base::SingleThreadTaskRunner> interface_ptr_task_runner_; + ProxyType proxy_; + InterfacePtrType<Interface> interface_ptr_; + base::WeakPtrFactory<ThreadSafeInterfacePtrBase> weak_ptr_factory_; +}; + +struct ThreadSafeInterfacePtrDeleter { + template <typename Interface, template <typename> class InterfacePtrType> + static void Destruct( + const ThreadSafeInterfacePtrBase<Interface, InterfacePtrType>* + interface_ptr) { + interface_ptr->DeleteOnCorrectThread(); + } +}; + +template <typename Interface> +using ThreadSafeAssociatedInterfacePtr = + ThreadSafeInterfacePtrBase<Interface, AssociatedInterfacePtr>; + +template <typename Interface> +using ThreadSafeInterfacePtr = + ThreadSafeInterfacePtrBase<Interface, InterfacePtr>; + +} // namespace mojo + +#endif // MOJO_PUBLIC_CPP_BINDINGS_THREAD_SAFE_INTERFACE_PTR_BASE_H_ diff --git a/chromium/mojo/public/cpp/system/platform_handle.h b/chromium/mojo/public/cpp/system/platform_handle.h index f30f2cfd8ec..4e33f1b1baf 100644 --- a/chromium/mojo/public/cpp/system/platform_handle.h +++ b/chromium/mojo/public/cpp/system/platform_handle.h @@ -62,6 +62,7 @@ MojoResult UnwrapPlatformFile(ScopedHandle handle, base::PlatformFile* file); // SharedMemoryHandle. Note that |read_only| is only an indicator of whether // |memory_handle| only supports read-only mapping. It does NOT have any // influence on the access control of the shared buffer object. +MOJO_CPP_SYSTEM_EXPORT ScopedSharedBufferHandle WrapSharedMemoryHandle( const base::SharedMemoryHandle& memory_handle, size_t size, diff --git a/chromium/mojo/public/java/BUILD.gn b/chromium/mojo/public/java/BUILD.gn index 8762db3985c..078064114e6 100644 --- a/chromium/mojo/public/java/BUILD.gn +++ b/chromium/mojo/public/java/BUILD.gn @@ -4,7 +4,7 @@ import("//build/config/android/rules.gni") -android_library("system") { +android_library("system_java") { java_files = [ "system/src/org/chromium/mojo/system/Core.java", "system/src/org/chromium/mojo/system/DataPipe.java", @@ -23,7 +23,7 @@ android_library("system") { ] } -android_library("bindings") { +android_library("bindings_java") { java_files = [ "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceNotSupported.java", "bindings/src/org/chromium/mojo/bindings/AssociatedInterfaceRequestNotSupported.java", @@ -56,7 +56,7 @@ android_library("bindings") { ] deps = [ - ":system", + ":system_java", "//base:base_java", ] diff --git a/chromium/mojo/public/js/threading.js b/chromium/mojo/public/js/threading.js index cfe50376e19..49ab5c91429 100644 --- a/chromium/mojo/public/js/threading.js +++ b/chromium/mojo/public/js/threading.js @@ -7,7 +7,7 @@ // Note: This file is for documentation purposes only. The code here is not // actually executed. The real module is implemented natively in Mojo. // -// This module provides a way for a Mojo application implemented in JS +// This module provides a way for a Service implemented in JS // to exit by quitting the current message loop. This module is not // intended to be used by Mojo JS application started by the JS // content handler. diff --git a/chromium/mojo/public/js/validation_unittests.js b/chromium/mojo/public/js/validation_unittests.js index 4bdaa8f3e2d..0eb0e4dc5ac 100644 --- a/chromium/mojo/public/js/validation_unittests.js +++ b/chromium/mojo/public/js/validation_unittests.js @@ -38,7 +38,7 @@ define([ TestMessageParserFailure.prototype.toString = function() { return 'Error: ' + this.message + ' for "' + this.input + '"'; - } + }; function checkData(data, expectedData, input) { if (data.byteLength != expectedData.byteLength) { @@ -225,8 +225,6 @@ define([ for (var i = 0; i < testFiles.length; i++) { // TODO(hansmuller) Temporarily skipping array pointer overflow tests // because JS numbers are limited to 53 bits. - // TODO(yzshen) Skipping struct versioning tests (tests with "mthd11" - // in the name) because the feature is not supported in JS yet. // TODO(rudominer): Temporarily skipping 'no-such-method', // 'invalid_request_flags', and 'invalid_response_flags' until additional // logic in *RequestValidator and *ResponseValidator is ported from @@ -234,7 +232,6 @@ define([ // TODO(crbug/640298): Implement max recursion depth for JS. // TODO(crbug/628104): Support struct map keys for JS. if (testFiles[i].indexOf("overflow") != -1 || - testFiles[i].indexOf("mthd11") != -1 || testFiles[i].indexOf("conformance_mthd19") != -1 || testFiles[i].indexOf("conformance_mthd20") != -1 || testFiles[i].indexOf("no_such_method") != -1 || @@ -302,7 +299,7 @@ define([ var validationError = noError; testConnection.router_.validationErrorHandler = function(err) { validationError = err; - } + }; testConnection.router_.connector_.waitForNextMessage(); checkValidationResult(testFiles[i], validationError); diff --git a/chromium/mojo/public/js/validator.js b/chromium/mojo/public/js/validator.js index 48e36c61d22..a3f4f005f5c 100644 --- a/chromium/mojo/public/js/validator.js +++ b/chromium/mojo/public/js/validator.js @@ -81,7 +81,7 @@ define("mojo/public/js/validator", [ return false; return true; - } + }; Validator.prototype.claimRange = function(start, numBytes) { if (this.isValidRange(start, numBytes)) { @@ -89,7 +89,7 @@ define("mojo/public/js/validator", [ return true; } return false; - } + }; Validator.prototype.claimHandle = function(index) { if (index === codec.kEncodedInvalidHandleValue) @@ -101,7 +101,7 @@ define("mojo/public/js/validator", [ // This is safe because handle indices are uint32. this.handleIndex = index + 1; return true; - } + }; Validator.prototype.validateEnum = function(offset, enumClass, nullable) { // Note: Assumes that enums are always 32 bits! But this matches @@ -120,14 +120,13 @@ define("mojo/public/js/validator", [ if (!this.claimHandle(index)) return validationError.ILLEGAL_HANDLE; return validationError.NONE; - } + }; Validator.prototype.validateInterface = function(offset, nullable) { return this.validateHandle(offset, nullable); - } + }; - Validator.prototype.validateStructHeader = - function(offset, minNumBytes, minVersion) { + Validator.prototype.validateStructHeader = function(offset, minNumBytes) { if (!codec.isAligned(offset)) return validationError.MISALIGNED_OBJECT; @@ -135,20 +134,44 @@ define("mojo/public/js/validator", [ return validationError.ILLEGAL_MEMORY_RANGE; var numBytes = this.message.buffer.getUint32(offset); - var version = this.message.buffer.getUint32(offset + 4); - // Backward compatibility is not yet supported. - if (numBytes < minNumBytes || version < minVersion) + if (numBytes < minNumBytes) return validationError.UNEXPECTED_STRUCT_HEADER; if (!this.claimRange(offset, numBytes)) return validationError.ILLEGAL_MEMORY_RANGE; return validationError.NONE; - } + }; + + Validator.prototype.validateStructVersion = function(offset, versionSizes) { + var numBytes = this.message.buffer.getUint32(offset); + var version = this.message.buffer.getUint32(offset + 4); + + if (version <= versionSizes[versionSizes.length - 1].version) { + // Scan in reverse order to optimize for more recent versionSizes. + for (var i = versionSizes.length - 1; i >= 0; --i) { + if (version >= versionSizes[i].version) { + if (numBytes == versionSizes[i].numBytes) + break; + return validationError.UNEXPECTED_STRUCT_HEADER; + } + } + } else if (numBytes < versionSizes[versionSizes.length-1].numBytes) { + return validationError.UNEXPECTED_STRUCT_HEADER; + } + + return validationError.NONE; + }; + + Validator.prototype.isFieldInStructVersion = function(offset, fieldVersion) { + var structVersion = this.message.buffer.getUint32(offset + 4); + return fieldVersion <= structVersion; + }; Validator.prototype.validateMessageHeader = function() { - var err = this.validateStructHeader(0, codec.kMessageHeaderSize, 0); + + var err = this.validateStructHeader(0, codec.kMessageHeaderSize); if (err != validationError.NONE) return err; @@ -174,7 +197,7 @@ define("mojo/public/js/validator", [ return validationError.MESSAGE_HEADER_INVALID_FLAGS; return validationError.NONE; - } + }; // Returns the message.buffer relative offset this pointer "points to", // NULL_MOJO_POINTER if the pointer represents a null, or JS null if the @@ -185,7 +208,7 @@ define("mojo/public/js/validator", [ return NULL_MOJO_POINTER; var bufferOffset = offset + pointerValue; return Number.isSafeInteger(bufferOffset) ? bufferOffset : null; - } + }; Validator.prototype.decodeUnionSize = function(offset) { return this.message.buffer.getUint32(offset); @@ -208,7 +231,7 @@ define("mojo/public/js/validator", [ return this.validateArray(arrayOffset, elementSize, elementType, expectedDimensionSizes, currentDimension); - } + }; Validator.prototype.validateStructPointer = function( offset, structClass, nullable) { @@ -221,7 +244,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; return structClass.validate(this, structOffset); - } + }; Validator.prototype.validateUnion = function( offset, unionClass, nullable) { @@ -232,7 +255,7 @@ define("mojo/public/js/validator", [ } return unionClass.validate(this, offset); - } + }; Validator.prototype.validateNestedUnion = function( offset, unionClass, nullable) { @@ -245,7 +268,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_UNION; return this.validateUnion(unionOffset, unionClass, nullable); - } + }; // This method assumes that the array at arrayPointerOffset has // been validated. @@ -253,7 +276,7 @@ define("mojo/public/js/validator", [ Validator.prototype.arrayLength = function(arrayPointerOffset) { var arrayOffset = this.decodePointer(arrayPointerOffset); return this.message.buffer.getUint32(arrayOffset + 4); - } + }; Validator.prototype.validateMapPointer = function( offset, mapIsNullable, keyClass, valueClass, valueIsNullable) { @@ -268,7 +291,7 @@ define("mojo/public/js/validator", [ validationError.NONE : validationError.UNEXPECTED_NULL_POINTER; var mapEncodedSize = codec.kStructHeaderSize + codec.kMapStructPayloadSize; - var err = this.validateStructHeader(structOffset, mapEncodedSize, 0); + var err = this.validateStructHeader(structOffset, mapEncodedSize); if (err !== validationError.NONE) return err; @@ -301,12 +324,12 @@ define("mojo/public/js/validator", [ return validationError.DIFFERENT_SIZED_ARRAYS_IN_MAP; return validationError.NONE; - } + }; Validator.prototype.validateStringPointer = function(offset, nullable) { return this.validateArrayPointer( offset, codec.Uint8.encodedSize, codec.Uint8, nullable, [0], 0); - } + }; // Similar to Array_Data<T>::Validate() // mojo/public/cpp/bindings/lib/array_internal.h @@ -363,7 +386,7 @@ define("mojo/public/js/validator", [ return this.validateEnum(elementsOffset, elementType.cls, nullable); return validationError.NONE; - } + }; // Note: the |offset + i * elementSize| computation in the validateFooElements // methods below is "safe" because elementSize <= 8, offset and @@ -379,7 +402,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateInterfaceElements = function(offset, numElements, nullable) { @@ -391,7 +414,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; // The elementClass parameter is the element type of the element arrays. Validator.prototype.validateArrayElements = @@ -407,7 +430,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; Validator.prototype.validateStructElements = function(offset, numElements, structClass, nullable) { @@ -420,7 +443,7 @@ define("mojo/public/js/validator", [ return err; } return validationError.NONE; - } + }; var exports = {}; exports.validationError = validationError; diff --git a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni index 2250cb1d91a..7d270fb4e65 100644 --- a/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni +++ b/chromium/mojo/public/tools/bindings/chromium_bindings_configuration.gni @@ -10,15 +10,18 @@ _typemap_imports = [ "//components/metrics/public/cpp/typemaps.gni", "//components/typemaps.gni", "//content/common/bluetooth/typemaps.gni", + "//content/common/indexed_db/typemaps.gni", "//content/common/typemaps.gni", "//content/public/common/typemaps.gni", "//device/bluetooth/public/interfaces/typemaps.gni", "//device/generic_sensor/public/interfaces/typemaps.gni", "//gpu/ipc/common/typemaps.gni", + "//media/capture/mojo/typemaps.gni", "//media/mojo/interfaces/typemaps.gni", "//mojo/common/typemaps.gni", "//mojo/public/cpp/bindings/tests/chromium_typemaps.gni", - "//services/shell/public/cpp/typemaps.gni", + "//net/interfaces/typemaps.gni", + "//services/service_manager/public/cpp/typemaps.gni", "//services/ui/public/interfaces/display/typemaps.gni", "//services/video_capture/public/interfaces/typemaps.gni", "//skia/public/interfaces/typemaps.gni", @@ -27,10 +30,13 @@ _typemap_imports = [ "//ui/events/devices/mojo/typemaps.gni", "//ui/events/mojo/typemaps.gni", "//ui/gfx/typemaps.gni", + "//ui/message_center/mojo/typemaps.gni", "//url/mojo/typemaps.gni", ] -_typemaps = [] +_typemap_imports_mac = [ "//content/common/typemaps_mac.gni" ] + +_typemaps = [] foreach(typemap_import, _typemap_imports) { # Avoid reassignment error by assigning to empty scope first. _imported = { @@ -43,3 +49,16 @@ typemaps = [] foreach(typemap, _typemaps) { typemaps += [ read_file(typemap, "scope") ] } + +_typemaps_mac = [] +foreach(typemap_import, _typemap_imports_mac) { + _imported = { + } + _imported = read_file(typemap_import, "scope") + _typemaps_mac += _imported.typemaps +} + +typemaps_mac = [] +foreach(typemap, _typemaps_mac) { + typemaps_mac += [ read_file(typemap, "scope") ] +} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl index c6b8c6d01cb..091fe09a3ba 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_declaration.tmpl @@ -1,5 +1,7 @@ {%- import "interface_macros.tmpl" as interface_macros %} class {{interface.name}}Proxy; + +template <typename ImplRefTraits> class {{interface.name}}Stub; class {{interface.name}}RequestValidator; @@ -16,7 +18,9 @@ class {{export_attribute}} {{interface.name}} static const bool HasSyncMethods_ = {% if interface|has_sync_methods %}true{% else %}false{% endif %}; using Proxy_ = {{interface.name}}Proxy; - using Stub_ = {{interface.name}}Stub; + + template <typename ImplRefTraits> + using Stub_ = {{interface.name}}Stub<ImplRefTraits>; using RequestValidator_ = {{interface.name}}RequestValidator; {%- if interface|has_callbacks %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl index 23908c26a60..a60eea39f0d 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_definition.tmpl @@ -159,19 +159,21 @@ bool {{class_name}}_{{method.name}}_ForwardToCallback::Accept( {%- if method.sync %} bool {{proxy_name}}::{{method.name}}( {{interface_macros.declare_sync_method_params("param_", method)}}) { + mojo::internal::SerializationContext serialization_context( + group_controller_); {{struct_macros.get_serialized_size(params_struct, "param_%s", - "&serialization_context_")}} + "&serialization_context")}} mojo::internal::RequestMessageBuilder builder({{message_name}}, size, mojo::Message::kFlagIsSync); {{build_message(params_struct, "param_%s", params_description, - "&serialization_context_")}} + "&serialization_context")}} bool result = false; mojo::MessageReceiver* responder = new {{class_name}}_{{method.name}}_HandleSyncResponse( - serialization_context_.group_controller, &result + group_controller_, &result {%- for param in method.response_parameters -%} , param_{{param.name}} {%- endfor %}); @@ -183,8 +185,10 @@ bool {{proxy_name}}::{{method.name}}( void {{proxy_name}}::{{method.name}}( {{interface_macros.declare_request_params("in_", method)}}) { + mojo::internal::SerializationContext serialization_context( + group_controller_); {{struct_macros.get_serialized_size(params_struct, "in_%s", - "&serialization_context_")}} + "&serialization_context")}} {%- if method.response_parameters != None %} mojo::internal::RequestMessageBuilder builder({{message_name}}, size); @@ -193,12 +197,12 @@ void {{proxy_name}}::{{method.name}}( {%- endif %} {{build_message(params_struct, "in_%s", params_description, - "&serialization_context_")}} + "&serialization_context")}} {%- if method.response_parameters != None %} mojo::MessageReceiver* responder = new {{class_name}}_{{method.name}}_ForwardToCallback( - callback, serialization_context_.group_controller); + callback, group_controller_); if (!receiver_->AcceptWithResponder(builder.message(), responder)) delete responder; {%- else %} @@ -236,7 +240,7 @@ class {{class_name}}_{{method.name}}_ProxyToResponder { ~{{class_name}}_{{method.name}}_ProxyToResponder() { #if DCHECK_IS_ON() if (responder_) { - // Is the Mojo application destroying the callback without running it + // Is the Service destroying the callback without running it // and without first closing the pipe? responder_->DCheckInvalid("The callback passed to " "{{class_name}}::{{method.name}}() was never run."); @@ -295,15 +299,13 @@ void {{class_name}}_{{method.name}}_ProxyToResponder::Run( {%- endif -%} {%- endfor %} -{{class_name}}Stub::{{class_name}}Stub() - : sink_(nullptr) { -} - -{{class_name}}Stub::~{{interface.name}}Stub() {} +{#--- StubDispatch definition #} -{#--- Stub definition #} - -bool {{class_name}}Stub::Accept(mojo::Message* message) { +// static +bool {{class_name}}StubDispatch::Accept( + {{interface.name}}* impl, + mojo::internal::SerializationContext* context, + mojo::Message* message) { {%- if interface.methods %} switch (message->header()->name) { {%- for method in interface.methods %} @@ -314,13 +316,13 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { message->mutable_payload()); {%- set desc = class_name~"::"~method.name %} - {{alloc_params(method.param_struct, "params", "message", - "&serialization_context_", desc)|indent(4)}} - // A null |sink_| means no implementation was bound. - assert(sink_); + {{alloc_params(method.param_struct, "params", "message", "context", desc)| + indent(4)}} + // A null |impl| means no implementation was bound. + assert(impl); TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); mojo::internal::MessageDispatchContext context(message); - sink_->{{method.name}}({{pass_params(method.parameters)}}); + impl->{{method.name}}({{pass_params(method.parameters)}}); return true; {%- else %} break; @@ -332,8 +334,12 @@ bool {{class_name}}Stub::Accept(mojo::Message* message) { return false; } -bool {{class_name}}Stub::AcceptWithResponder( - mojo::Message* message, mojo::MessageReceiverWithStatus* responder) { +// static +bool {{class_name}}StubDispatch::AcceptWithResponder( + {{interface.name}}* impl, + mojo::internal::SerializationContext* context, + mojo::Message* message, + mojo::MessageReceiverWithStatus* responder) { {%- if interface.methods %} switch (message->header()->name) { {%- for method in interface.methods %} @@ -344,20 +350,18 @@ bool {{class_name}}Stub::AcceptWithResponder( message->mutable_payload()); {%- set desc = class_name~"::"~method.name %} - {{alloc_params(method.param_struct, "params", "message", - "&serialization_context_", desc)| - indent(4)}} + {{alloc_params(method.param_struct, "params", "message", "context", desc)| + indent(4)}} {{class_name}}::{{method.name}}Callback callback = {{class_name}}_{{method.name}}_ProxyToResponder::CreateCallback( message->request_id(), - message->has_flag(mojo::Message::kFlagIsSync), - responder, - serialization_context_.group_controller); - // A null |sink_| means no implementation was bound. - assert(sink_); + message->has_flag(mojo::Message::kFlagIsSync), responder, + context->group_controller); + // A null |impl| means no implementation was bound. + assert(impl); TRACE_EVENT0("mojom", "{{class_name}}::{{method.name}}"); mojo::internal::MessageDispatchContext context(message); - sink_->{{method.name}}( + impl->{{method.name}}( {%- if method.parameters -%}{{pass_params(method.parameters)}}, {% endif -%}callback); return true; {%- else %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl index cfc4ef6770d..91726e580db 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_proxy_declaration.tmpl @@ -11,11 +11,11 @@ class {{export_attribute}} {{interface.name}}Proxy void {{method.name}}({{interface_macros.declare_request_params("", method)}}) override; {%- endfor %} - mojo::internal::SerializationContext* serialization_context() { - return &serialization_context_; + void set_group_controller( + scoped_refptr<mojo::AssociatedGroupController> group_controller) { + group_controller_ = std::move(group_controller); } - private: mojo::MessageReceiverWithResponder* receiver_; - mojo::internal::SerializationContext serialization_context_; + scoped_refptr<mojo::AssociatedGroupController> group_controller_; }; diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl index 8f1a45c3f43..a8296b71fe9 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/interface_stub_declaration.tmpl @@ -1,18 +1,49 @@ -class {{export_attribute}} {{interface.name}}Stub : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) { +class {{export_attribute}} {{interface.name}}StubDispatch { public: - {{interface.name}}Stub(); - ~{{interface.name}}Stub() override; - void set_sink({{interface.name}}* sink) { sink_ = sink; } - {{interface.name}}* sink() { return sink_; } + static bool Accept({{interface.name}}* impl, + mojo::internal::SerializationContext* context, + mojo::Message* message); + static bool AcceptWithResponder({{interface.name}}* impl, + mojo::internal::SerializationContext* context, + mojo::Message* message, + mojo::MessageReceiverWithStatus* responder); +}; + +template <typename ImplRefTraits = + mojo::RawPtrImplRefTraits<{{interface.name}}>> +class {{interface.name}}Stub + : public NON_EXPORTED_BASE(mojo::MessageReceiverWithResponderStatus) { + public: + using ImplPointerType = typename ImplRefTraits::PointerType; + + {{interface.name}}Stub() {} + ~{{interface.name}}Stub() override {} + + void set_sink(ImplPointerType sink) { sink_ = std::move(sink); } + ImplPointerType& sink() { return sink_; } + mojo::internal::SerializationContext* serialization_context() { return &serialization_context_; } - bool Accept(mojo::Message* message) override; - bool AcceptWithResponder(mojo::Message* message, - mojo::MessageReceiverWithStatus* responder) override; + bool Accept(mojo::Message* message) override { + if (ImplRefTraits::IsNull(sink_)) + return false; + return {{interface.name}}StubDispatch::Accept( + ImplRefTraits::GetRawPointer(&sink_), &serialization_context_, message); + } + + bool AcceptWithResponder( + mojo::Message* message, + mojo::MessageReceiverWithStatus* responder) override { + if (ImplRefTraits::IsNull(sink_)) + return false; + return {{interface.name}}StubDispatch::AcceptWithResponder( + ImplRefTraits::GetRawPointer(&sink_), &serialization_context_, message, + responder); + } private: - {{interface.name}}* sink_; + ImplPointerType sink_; mojo::internal::SerializationContext serialization_context_; }; diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl index bb5acd93433..f718276c2a9 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared-internal.h.tmpl @@ -50,7 +50,8 @@ class {{union.name}}_Data; {%- from "enum_macros.tmpl" import enum_data_decl -%} {%- for enum in all_enums %} {%- if enum|is_native_only_kind %} -using {{enum.name}}_Data = mojo::internal::NativeEnum_Data; +using {{enum|get_name_for_kind(flatten_nested_kind=True)}}_Data = + mojo::internal::NativeEnum_Data; {%- else %} {{enum_data_decl(enum)}} {%- endif %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl index 4b18a218709..da99eb380be 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module-shared.h.tmpl @@ -97,7 +97,7 @@ namespace internal { {%- from "enum_macros.tmpl" import enum_decl%} {%- for enum in all_enums %} {%- if enum|is_native_only_kind %} -using {{enum.name}} = mojo::NativeEnum; +using {{enum|get_name_for_kind(flatten_nested_kind=True)}} = mojo::NativeEnum; {%- else %} {{enum_decl(enum)}} {%- endif %} diff --git a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl index f89d207409f..7fcc2888b8d 100644 --- a/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/cpp_templates/module.h.tmpl @@ -52,8 +52,10 @@ namespace {{variant}} { #include "mojo/public/cpp/bindings/map.h" #include "mojo/public/cpp/bindings/native_struct.h" #include "mojo/public/cpp/bindings/no_interface.h" +#include "mojo/public/cpp/bindings/raw_ptr_impl_ref_traits.h" #include "mojo/public/cpp/bindings/struct_ptr.h" #include "mojo/public/cpp/bindings/struct_traits.h" +#include "mojo/public/cpp/bindings/thread_safe_interface_ptr.h" #include "mojo/public/cpp/bindings/union_traits.h" #include "{{module.path}}-shared.h" {%- for import in imports %} @@ -110,9 +112,13 @@ extern const {{constant.kind|cpp_pod_type}} {{constant.name}}; class {{interface.name}}; using {{interface.name}}Ptr = mojo::InterfacePtr<{{interface.name}}>; using {{interface.name}}PtrInfo = mojo::InterfacePtrInfo<{{interface.name}}>; +using ThreadSafe{{interface.name}}Ptr = + mojo::ThreadSafeInterfacePtr<{{interface.name}}>; using {{interface.name}}Request = mojo::InterfaceRequest<{{interface.name}}>; using {{interface.name}}AssociatedPtr = mojo::AssociatedInterfacePtr<{{interface.name}}>; +using ThreadSafe{{interface.name}}AssociatedPtr = + mojo::ThreadSafeAssociatedInterfacePtr<{{interface.name}}>; using {{interface.name}}AssociatedPtrInfo = mojo::AssociatedInterfacePtrInfo<{{interface.name}}>; using {{interface.name}}AssociatedRequest = diff --git a/chromium/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl index aa3f6f6d9d2..4c0823cce6b 100644 --- a/chromium/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/java_templates/data_types_definition.tmpl @@ -1,19 +1,19 @@ -{% from "constant_definition.tmpl" import constant_def %} -{% from "enum_definition.tmpl" import enum_def %} +{%- from "constant_definition.tmpl" import constant_def %} +{%- from "enum_definition.tmpl" import enum_def %} {%- macro equality(kind, v1, v2, ne=False) -%} {%- if kind|is_reference_kind -%} {%- if kind|is_array_kind -%} {%- if kind.kind|is_reference_kind -%} -{% if ne %}!{% endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}}) +{%- if ne %}!{%- endif %}java.util.Arrays.deepEquals({{v1}}, {{v2}}) {%- else -%} -{% if ne %}!{% endif %}java.util.Arrays.equals({{v1}}, {{v2}}) +{%- if ne %}!{%- endif %}java.util.Arrays.equals({{v1}}, {{v2}}) {%- endif -%} {%- else -%} -{% if ne %}!{% endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}}) +{%- if ne %}!{%- endif %}org.chromium.mojo.bindings.BindingsHelper.equals({{v1}}, {{v2}}) {%- endif -%} {%- else -%} -{{v1}} {% if ne %}!={% else %}=={% endif %} {{v2}} +{{v1}} {%- if ne %}!={%- else %}=={%- endif %} {{v2}} {%- endif -%} {%- endmacro -%} @@ -37,27 +37,27 @@ org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE {%- endif -%} {%- endmacro -%} -{% macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %} -{% if kind|is_pointer_array_kind or kind|is_union_array_kind %} -{% set sub_kind = kind.kind %} -{% if check_for_null %} +{%- macro encode(variable, kind, offset, bit, level=0, check_for_null=True) %} +{%- if kind|is_pointer_array_kind or kind|is_union_array_kind %} +{%- set sub_kind = kind.kind %} +{%- if check_for_null %} if ({{variable}} == null) { encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}}); } else { -{% else %} +{%- else %} { -{% endif %} -{% if kind|is_pointer_array_kind %} -{% set encodePointer = 'encodePointerArray' %} -{% else %} -{% set encodePointer = 'encodeUnionArray' %} -{% endif %} +{%- endif %} +{%- if kind|is_pointer_array_kind %} +{%- set encodePointer = 'encodePointerArray' %} +{%- else %} +{%- set encodePointer = 'encodeUnionArray' %} +{%- endif %} org.chromium.mojo.bindings.Encoder encoder{{level + 1}} = encoder{{level}}.{{encodePointer}}({{variable}}.length, {{offset}}, {{kind|array_expected_length}}); for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) { {{encode(variable~'[i'~level~']', sub_kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(sub_kind) ~ ' * i'~level, 0, level+1)|indent(8)}} } } -{% elif kind|is_map_kind %} +{%- elif kind|is_map_kind %} if ({{variable}} == null) { encoder{{level}}.encodeNullPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}}); } else { @@ -74,28 +74,28 @@ if ({{variable}} == null) { {{encode('keys'~level, kind.key_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0, level+1, False)|indent(4)}} {{encode('values'~level, kind.value_kind|array, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + org.chromium.mojo.bindings.BindingsHelper.POINTER_SIZE', 0, level+1, False)|indent(4)}} } -{% else %} +{%- else %} encoder{{level}}.{{kind|encode_method(variable, offset, bit)}}; -{% endif %} -{% endmacro %} +{%- endif %} +{%- endmacro %} -{% macro decode(variable, kind, offset, bit, level=0) %} -{% if kind|is_struct_kind or +{%- macro decode(variable, kind, offset, bit, level=0) %} +{%- if kind|is_struct_kind or kind|is_pointer_array_kind or kind|is_union_array_kind or kind|is_map_kind %} org.chromium.mojo.bindings.Decoder decoder{{level+1}} = decoder{{level}}.readPointer({{offset}}, {{kind|is_nullable_kind|java_true_false}}); -{% if kind|is_struct_kind %} +{%- if kind|is_struct_kind %} {{variable}} = {{kind|java_type}}.decode(decoder{{level+1}}); -{% else %}{# kind|is_pointer_array_kind or is_map_kind #} -{% if kind|is_nullable_kind %} +{%- else %}{# kind|is_pointer_array_kind or is_map_kind #} +{%- if kind|is_nullable_kind %} if (decoder{{level+1}} == null) { {{variable}} = null; } else { -{% else %} +{%- else %} { -{% endif %} -{% if kind|is_map_kind %} +{%- endif %} +{%- if kind|is_map_kind %} decoder{{level+1}}.readDataHeaderForMap(); {{kind.key_kind|java_type}}[] keys{{level}}; {{kind.value_kind|java_type}}[] values{{level}}; @@ -109,69 +109,69 @@ if (decoder{{level+1}} == null) { for (int index{{level}} = 0; index{{level}} < keys{{level}}.length; ++index{{level}}) { {{variable}}.put(keys{{level}}[index{{level}}], values{{level}}[index{{level}}]); } -{% else %} +{%- else %} org.chromium.mojo.bindings.DataHeader si{{level+1}} = decoder{{level+1}}.readDataHeaderForPointerArray({{kind|array_expected_length}}); {{variable}} = {{kind|new_array('si'~(level+1)~'.elementsOrVersion')}}; for (int i{{level+1}} = 0; i{{level+1}} < si{{level+1}}.elementsOrVersion; ++i{{level+1}}) { {{decode(variable~'[i'~(level+1)~']', kind.kind, 'org.chromium.mojo.bindings.DataHeader.HEADER_SIZE + ' ~ array_element_size(kind.kind) ~' * i'~(level+1), 0, level+1)|indent(8)}} } -{% endif %} +{%- endif %} } -{% endif %} -{% elif kind|is_union_kind %} +{%- endif %} +{%- elif kind|is_union_kind %} {{variable}} = {{kind|java_type}}.decode(decoder{{level}}, {{offset}}); -{% else %} +{%- else %} {{variable}} = decoder{{level}}.{{kind|decode_method(offset, bit)}}; -{% if kind|is_array_kind and kind.kind|is_enum_kind %} -{% if kind|is_nullable_kind %} +{%- if kind|is_array_kind and kind.kind|is_enum_kind %} +{%- if kind|is_nullable_kind %} if ({{variable}} != null) { -{% else %} +{%- else %} { -{% endif %} +{%- endif %} for (int i{{level}} = 0; i{{level}} < {{variable}}.length; ++i{{level}}) { {{kind.kind|java_class_for_enum}}.validate({{variable}}[i{{level}}]); } } -{% elif kind|is_enum_kind %} +{%- elif kind|is_enum_kind %} {{kind|java_class_for_enum}}.validate({{variable}}); -{% endif %} -{% endif %} -{% endmacro %} +{%- endif %} +{%- endif %} +{%- endmacro %} -{% macro struct_def(struct, inner_class=False) %} +{%- macro struct_def(struct, inner_class=False) %} {{'static' if inner_class else 'public'}} final class {{struct|name}} extends org.chromium.mojo.bindings.Struct { private static final int STRUCT_SIZE = {{struct.versions[-1].num_bytes}}; private static final org.chromium.mojo.bindings.DataHeader[] VERSION_ARRAY = new org.chromium.mojo.bindings.DataHeader[] { {%- for version in struct.versions -%} - new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){% if not loop.last %}, {% endif -%} + new org.chromium.mojo.bindings.DataHeader({{version.num_bytes}}, {{version.version}}){%- if not loop.last %}, {%- endif -%} {%- endfor -%} }; private static final org.chromium.mojo.bindings.DataHeader DEFAULT_STRUCT_INFO = VERSION_ARRAY[{{struct.versions|length - 1}}]; -{% for constant in struct.constants %} +{%- for constant in struct.constants %} {{constant_def(constant)|indent(4)}} -{% endfor %} -{% for enum in struct.enums %} +{%- endfor %} +{%- for enum in struct.enums %} {{enum_def(enum, false)|indent(4)}} -{% endfor %} -{% if struct.fields %} +{%- endfor %} +{%- if struct.fields %} -{% for field in struct.fields %} +{%- for field in struct.fields %} public {{field.kind|java_type}} {{field|name}}; -{% endfor %} -{% endif %} +{%- endfor %} +{%- endif %} private {{struct|name}}(int version) { super(STRUCT_SIZE, version); -{% for field in struct.fields %} -{% if field.default %} +{%- for field in struct.fields %} +{%- if field.default %} {{field|name}} = {{field|default_value}}; -{% elif field.kind|is_any_handle_kind %} +{%- elif field.kind|is_any_handle_kind %} {{field|name}} = org.chromium.mojo.system.InvalidHandle.INSTANCE; -{% endif %} -{% endfor %} +{%- endif %} +{%- endfor %} } public {{struct|name}}() { @@ -200,31 +200,37 @@ if ({{variable}} != null) { if (decoder0 == null) { return null; } - org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); - {{struct|name}} result = new {{struct|name}}(mainDataHeader.elementsOrVersion); -{% for byte in struct.bytes %} -{% for packed_field in byte.packed_fields %} - if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) { - {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(12)}} + decoder0.increaseStackDepth(); + {{struct|name}} result; + try { + org.chromium.mojo.bindings.DataHeader mainDataHeader = decoder0.readAndValidateDataHeader(VERSION_ARRAY); + result = new {{struct|name}}(mainDataHeader.elementsOrVersion); +{%- for byte in struct.bytes %} +{%- for packed_field in byte.packed_fields %} + if (mainDataHeader.elementsOrVersion >= {{packed_field.min_version}}) { + {{decode('result.' ~ packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(16)}} + } +{%- endfor %} +{%- endfor %} + } finally { + decoder0.decreaseStackDepth(); } -{% endfor %} -{% endfor %} return result; } @SuppressWarnings("unchecked") @Override protected final void encode(org.chromium.mojo.bindings.Encoder encoder) { -{% if not struct.bytes %} +{%- if not struct.bytes %} encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); -{% else %} +{%- else %} org.chromium.mojo.bindings.Encoder encoder0 = encoder.getEncoderAtDataOffset(DEFAULT_STRUCT_INFO); -{% endif %} -{% for byte in struct.bytes %} -{% for packed_field in byte.packed_fields %} +{%- endif %} +{%- for byte in struct.bytes %} +{%- for packed_field in byte.packed_fields %} {{encode(packed_field.field|name, packed_field.field.kind, 8+packed_field.offset, packed_field.bit)|indent(8)}} -{% endfor %} -{% endfor %} +{%- endfor %} +{%- endfor %} } /** @@ -238,13 +244,13 @@ if ({{variable}} != null) { return false; if (getClass() != object.getClass()) return false; -{% if struct.fields|length %} +{%- if struct.fields|length %} {{struct|name}} other = ({{struct|name}}) object; -{% for field in struct.fields %} +{%- for field in struct.fields %} if ({{equality(field.kind, 'this.'~field|name, 'other.'~field|name, True)}}) return false; -{% endfor %} -{% endif %} +{%- endfor %} +{%- endif %} return true; } @@ -255,28 +261,28 @@ if ({{variable}} != null) { public int hashCode() { final int prime = 31; int result = prime + getClass().hashCode(); -{% for field in struct.fields %} +{%- for field in struct.fields %} result = prime * result + {{hash(field.kind, field|name)}}; -{% endfor %} +{%- endfor %} return result; } } -{% endmacro %} +{%- endmacro %} -{% macro union_def(union) %} +{%- macro union_def(union) %} public final class {{union|name}} extends org.chromium.mojo.bindings.Union { public static final class Tag { -{% for field in union.fields %} +{%- for field in union.fields %} public static final int {{field|ucc}} = {{loop.index0}}; -{% endfor %} +{%- endfor %} }; private int mTag_ = -1; -{% for field in union.fields %} +{%- for field in union.fields %} private {{field.kind|java_type}} m{{field|ucc}}; -{% endfor %} +{%- endfor %} public int which() { return mTag_; @@ -285,7 +291,7 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { public boolean isUnknown() { return mTag_ == -1; } -{% for field in union.fields %} +{%- for field in union.fields %} // TODO(rockot): Fix the findbugs error and remove this suppression. // See http://crbug.com/570386. @@ -302,7 +308,7 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { assert mTag_ == Tag.{{field|ucc}}; return m{{field|ucc}}; } -{% endfor %} +{%- endfor %} @Override @@ -310,20 +316,20 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { encoder0.encode(org.chromium.mojo.bindings.BindingsHelper.UNION_SIZE, offset); encoder0.encode(mTag_, offset + 4); switch (mTag_) { -{% for field in union.fields %} +{%- for field in union.fields %} case Tag.{{field|ucc}}: { -{% if field.kind|is_union_kind %} +{%- if field.kind|is_union_kind %} if (m{{field|ucc}} == null) { encoder0.encodeNullPointer(offset + 8, {{field.kind|is_nullable_kind|java_true_false}}); } else { m{{field|ucc}}.encode(encoder0.encoderForUnionPointer(offset + 8), 0); } -{% else %} +{%- else %} {{encode('m' ~ field|ucc, field.kind, 'offset + 8', 0)|indent(16)}} -{% endif %} +{%- endif %} break; } -{% endfor %} +{%- endfor %} default: { break; } @@ -341,20 +347,20 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { } {{union|name}} result = new {{union|name}}(); switch (dataHeader.elementsOrVersion) { -{% for field in union.fields %} +{%- for field in union.fields %} case Tag.{{field|ucc}}: { -{% if field.kind|is_union_kind %} +{%- if field.kind|is_union_kind %} org.chromium.mojo.bindings.Decoder decoder1 = decoder0.readPointer(offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE, {{field.kind|is_nullable_kind|java_true_false}}); if (decoder1 != null) { result.m{{field|ucc}} = {{field.kind|name}}.decode(decoder1, 0); } -{% else %} +{%- else %} {{decode('result.m'~field|ucc, field.kind, 'offset + org.chromium.mojo.bindings.DataHeader.HEADER_SIZE', 0)|indent(16)}} -{% endif %} +{%- endif %} result.mTag_ = Tag.{{field|ucc}}; break; } -{% endfor %} +{%- endfor %} default: { break; } @@ -377,10 +383,10 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { if (mTag_ != other.mTag_) return false; switch (mTag_) { -{% for field in union.fields %} +{%- for field in union.fields %} case Tag.{{field|ucc}}: return {{equality(field.kind, 'm'~field|ucc, 'other.m'~field|ucc)}}; -{% endfor %} +{%- endfor %} default: break; } @@ -396,12 +402,12 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { int result = prime + getClass().hashCode(); result = prime * result + org.chromium.mojo.bindings.BindingsHelper.hashCode(mTag_); switch (mTag_) { -{% for field in union.fields %} +{%- for field in union.fields %} case Tag.{{field|ucc}}: { result = prime * result + {{hash(field.kind, 'm'~field|ucc)}}; break; } -{% endfor %} +{%- endfor %} default: { break; } @@ -409,4 +415,4 @@ public final class {{union|name}} extends org.chromium.mojo.bindings.Union { return result; } } -{% endmacro %} +{%- endmacro %} diff --git a/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl b/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl index b16d673deb1..e823e461558 100644 --- a/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl +++ b/chromium/mojo/public/tools/bindings/generators/js_templates/struct_definition.tmpl @@ -34,16 +34,40 @@ {{struct.name}}.validate = function(messageValidator, offset) { var err; - err = messageValidator.validateStructHeader(offset, {{struct.name}}.encodedSize, {{struct.versions[-1].version}}); + err = messageValidator.validateStructHeader(offset, codec.kStructHeaderSize); if (err !== validator.validationError.NONE) return err; + var kVersionSizes = [ +{%- for version in struct.versions %} + {version: {{version.version}}, numBytes: {{version.num_bytes}}}{% if not loop.last %}, + {%- endif -%} +{% endfor %} + ]; + err = messageValidator.validateStructVersion(offset, kVersionSizes); + if (err !== validator.validationError.NONE) + return err; + +{#- Before validating fields introduced at a certain version, we need to add + a version check, which makes sure we skip further validation if |object| + is from an earlier version. |last_checked_version| records the last + version that we have added such version check. #} {%- from "validation_macros.tmpl" import validate_struct_field %} -{%- for packed_field in struct.packed.packed_fields %} +{%- set last_checked_version = 0 %} +{%- for packed_field in struct.packed.packed_fields_in_ordinal_order %} {%- set offset = packed_field|field_offset %} {%- set field = packed_field.field %} {%- set name = struct.name ~ '.' ~ field.name %} +{% if field|is_object_field or field|is_any_handle_or_interface_field or + field|is_enum_field %} +{% if packed_field.min_version > last_checked_version %} +{% set last_checked_version = packed_field.min_version %} + // version check {{name}} + if (!messageValidator.isFieldInStructVersion(offset, {{packed_field.min_version}})) + return validator.validationError.NONE; +{%- endif -%} {{validate_struct_field(field, offset, name)|indent(4)}} +{%- endif %} {%- endfor %} return validator.validationError.NONE; diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py index 07eb45bfc3e..2cf791fab0f 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_cpp_generator.py @@ -196,6 +196,11 @@ def IsHashableKind(kind): not _current_typemap[GetFullMojomNameForKind(kind)]["hashable"]): return False return all(Check(field.kind) for field in kind.fields) + elif mojom.IsEnumKind(kind): + if (IsTypemappedKind(kind) and + not _current_typemap[GetFullMojomNameForKind(kind)]["hashable"]): + return False + return True elif mojom.IsUnionKind(kind): return all(Check(field.kind) for field in kind.fields) elif mojom.IsAnyHandleKind(kind): diff --git a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py index 27ac18a8274..e57f3305eba 100644 --- a/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py +++ b/chromium/mojo/public/tools/bindings/generators/mojom_js_generator.py @@ -353,33 +353,44 @@ def IsUnionField(field): def IsBoolField(field): return mojom.IsBoolKind(field.kind) +def IsObjectField(field): + return mojom.IsObjectKind(field.kind) + +def IsAnyHandleOrInterfaceField(field): + return mojom.IsAnyHandleOrInterfaceKind(field.kind) + +def IsEnumField(field): + return mojom.IsEnumKind(field.kind) + class Generator(generator.Generator): js_filters = { - "default_value": JavaScriptDefaultValue, - "payload_size": JavaScriptPayloadSize, "decode_snippet": JavaScriptDecodeSnippet, + "default_value": JavaScriptDefaultValue, "encode_snippet": JavaScriptEncodeSnippet, - "union_decode_snippet": JavaScriptUnionDecodeSnippet, - "union_encode_snippet": JavaScriptUnionEncodeSnippet, "expression_to_text": ExpressionToText, "field_offset": JavaScriptFieldOffset, "has_callbacks": mojom.HasCallbacks, + "is_any_handle_or_interface_field": IsAnyHandleOrInterfaceField, "is_array_pointer_field": IsArrayPointerField, "is_bool_field": IsBoolField, "is_enum_field": IsEnumField, - "is_map_pointer_field": IsMapPointerField, - "is_struct_pointer_field": IsStructPointerField, - "is_string_pointer_field": IsStringPointerField, - "is_union_field": IsUnionField, "is_handle_field": IsHandleField, "is_interface_field": IsInterfaceField, "is_interface_request_field": IsInterfaceRequestField, - "js_type": JavaScriptType, + "is_map_pointer_field": IsMapPointerField, + "is_object_field": IsObjectField, + "is_string_pointer_field": IsStringPointerField, + "is_struct_pointer_field": IsStructPointerField, + "is_union_field": IsUnionField, "js_proxy_method_parameter_value": JavaScriptProxyMethodParameterValue, "js_stub_method_parameter_value": JavaScriptStubMethodParameterValue, + "js_type": JavaScriptType, + "payload_size": JavaScriptPayloadSize, "stylize_method": generator.StudlyCapsToCamel, + "union_decode_snippet": JavaScriptUnionDecodeSnippet, + "union_encode_snippet": JavaScriptUnionEncodeSnippet, "validate_array_params": JavaScriptValidateArrayParams, "validate_enum_params": JavaScriptValidateEnumParams, "validate_handle_params": JavaScriptValidateHandleParams, diff --git a/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py b/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py deleted file mode 100755 index 4c6f5974069..00000000000 --- a/chromium/mojo/public/tools/bindings/generators/run_cpp_generator.py +++ /dev/null @@ -1,28 +0,0 @@ -#!/usr/bin/env python -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import ast -import os -import sys - -script_dir = os.path.dirname(os.path.realpath(__file__)) -sys.path.insert(0, os.path.join(script_dir, os.pardir, "pylib")) - -from mojom.generate.data -import mojom_cpp_generator - -def ReadDict(file): - with open(file, 'r') as f: - s = f.read() - dict = ast.literal_eval(s) - return dict - -dict = ReadDict(sys.argv[1]) -module = mojom.generate.data.ModuleFromData(dict) -dir = None -if len(sys.argv) > 2: - dir = sys.argv[2] -cpp = mojom_cpp_generator.Generator(module, ".", dir) -cpp.GenerateFiles([]) diff --git a/chromium/mojo/public/tools/bindings/mojom.gni b/chromium/mojo/public/tools/bindings/mojom.gni index be36cb49d62..b4495b3ec2d 100644 --- a/chromium/mojo/public/tools/bindings/mojom.gni +++ b/chromium/mojo/public/tools/bindings/mojom.gni @@ -12,16 +12,15 @@ mojom_generator_sources = [ "$mojom_generator_root/pylib/mojom/error.py", "$mojom_generator_root/pylib/mojom/generate/__init__.py", "$mojom_generator_root/pylib/mojom/generate/constant_resolver.py", - "$mojom_generator_root/pylib/mojom/generate/data.py", "$mojom_generator_root/pylib/mojom/generate/generator.py", "$mojom_generator_root/pylib/mojom/generate/module.py", "$mojom_generator_root/pylib/mojom/generate/pack.py", "$mojom_generator_root/pylib/mojom/generate/template_expander.py", + "$mojom_generator_root/pylib/mojom/generate/translate.py", "$mojom_generator_root/pylib/mojom/parse/__init__.py", "$mojom_generator_root/pylib/mojom/parse/ast.py", "$mojom_generator_root/pylib/mojom/parse/lexer.py", "$mojom_generator_root/pylib/mojom/parse/parser.py", - "$mojom_generator_root/pylib/mojom/parse/translate.py", "$mojom_generator_script", ] @@ -39,10 +38,15 @@ foreach(config_file, _bindings_configuration_files) { _bindings_configurations += [ read_file(config_file, "scope") ] } foreach(configuration, _bindings_configurations) { + # Check that the mojom field of each typemap refers to a mojom that exists. foreach(typemap, configuration.typemaps) { - # Check that the mojom field of each typemap refers to a mojom that exists. read_file(typemap.mojom, "") } + if (is_mac && defined(configuration.typemaps_mac)) { + foreach(typemap, configuration.typemaps_mac) { + read_file(typemap.mojom, "") + } + } } # Generate C++/JavaScript/Java source files from mojom files. The output files @@ -79,6 +83,9 @@ foreach(configuration, _bindings_configurations) { # - convert all users to use the new mode; # - remove support for the old mode. # +# cpp_only (optional) +# If set to true, only the C++ bindings targets will be generated. +# # The following parameters are used to support the component build. They are # needed so that bindings which are linked with a component can use the same # export settings for classes. The first three are for the chromium variant, and @@ -209,6 +216,9 @@ template("mojom") { # Generate code for variants. foreach(bindings_configuration, _bindings_configurations) { cpp_only = false + if (defined(invoker.cpp_only)) { + cpp_only = invoker.cpp_only + } variant_suffix = "" if (defined(bindings_configuration.variant)) { variant = bindings_configuration.variant @@ -258,6 +268,13 @@ template("mojom") { active_typemaps += [ typemap ] } } + if (is_mac && defined(bindings_configuration.typemaps_mac)) { + foreach(typemap, bindings_configuration.typemaps_mac) { + if (get_path_info(source, "abspath") == typemap.mojom) { + active_typemaps += [ typemap ] + } + } + } } if (!cpp_only) { @@ -515,8 +532,8 @@ template("mojom") { android_library(java_target_name) { deps = [ "//base:base_java", - "//mojo/public/java:bindings", - "//mojo/public/java:system", + "//mojo/public/java:bindings_java", + "//mojo/public/java:system_java", ] foreach(d, all_deps) { diff --git a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py index 93bcaf34737..fd77adcd994 100755 --- a/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py +++ b/chromium/mojo/public/tools/bindings/mojom_bindings_generator.py @@ -37,10 +37,9 @@ sys.path.insert(0, os.path.join(os.path.dirname(os.path.abspath(__file__)), from mojom.error import Error import mojom.fileutil as fileutil -from mojom.generate.data import OrderedModuleFromData +from mojom.generate import translate from mojom.generate import template_expander from mojom.parse.parser import Parse -from mojom.parse.translate import Translate _BUILTIN_GENERATORS = { @@ -102,6 +101,12 @@ def FindImportFile(rel_dir, file_name, search_rel_dirs): class MojomProcessor(object): + """Parses mojom files and creates ASTs for them. + + Attributes: + _processed_files: {Dict[str, mojom.generate.module.Module]} Mapping from + relative mojom filename paths to the module AST for that mojom file. + """ def __init__(self, should_generate): self._should_generate = should_generate self._processed_files = {} @@ -136,20 +141,18 @@ class MojomProcessor(object): tree = self._parsed_files[rel_filename.path] dirname, name = os.path.split(rel_filename.path) - mojom = Translate(tree, name) - if args.debug_print_intermediate: - pprint.PrettyPrinter().pprint(mojom) # Process all our imports first and collect the module object for each. # We use these to generate proper type info. - for import_data in mojom['imports']: + imports = {} + for parsed_imp in tree.import_list: rel_import_file = FindImportFile( RelativePath(dirname, rel_filename.source_root), - import_data['filename'], args.import_directories) - import_data['module'] = self._GenerateModule( + parsed_imp.import_filename, args.import_directories) + imports[parsed_imp.import_filename] = self._GenerateModule( args, remaining_args, generator_modules, rel_import_file) - module = OrderedModuleFromData(mojom) + module = translate.OrderedModule(tree, name, imports) # Set the path as relative to the source root. module.path = rel_filename.relative_path() @@ -261,9 +264,6 @@ def main(): generate_parser.add_argument("-o", "--output_dir", dest="output_dir", default=".", help="output directory for generated files") - generate_parser.add_argument("--debug_print_intermediate", - action="store_true", - help="print the intermediate representation") generate_parser.add_argument("-g", "--generators", dest="generators_string", metavar="GENERATORS", diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py deleted file mode 100644 index 9ceb2e9b288..00000000000 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data.py +++ /dev/null @@ -1,530 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -# TODO(vtl): "data" is a pretty vague name. Rename it? - -import copy - -import module as mojom - -# This module provides a mechanism to turn mojom Modules to dictionaries and -# back again. This can be used to persist a mojom Module created progromatically -# or to read a dictionary from code or a file. -# Example: -# test_dict = { -# 'name': 'test', -# 'namespace': 'testspace', -# 'structs': [{ -# 'name': 'teststruct', -# 'fields': [ -# {'name': 'testfield1', 'kind': 'i32'}, -# {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}], -# 'interfaces': [{ -# 'name': 'Server', -# 'methods': [{ -# 'name': 'Foo', -# 'parameters': [{ -# 'name': 'foo', 'kind': 'i32'}, -# {'name': 'bar', 'kind': 'a:x:teststruct'}], -# 'ordinal': 42}]}] -# } -# test_module = data.ModuleFromData(test_dict) - -# Used to create a subclass of str that supports sorting by index, to make -# pretty printing maintain the order. -def istr(index, string): - class IndexedString(str): - def __lt__(self, other): - return self.__index__ < other.__index__ - - rv = IndexedString(string) - rv.__index__ = index - return rv - -def AddOptional(dictionary, key, value): - if value is not None: - dictionary[key] = value; - -builtin_values = frozenset([ - "double.INFINITY", - "double.NEGATIVE_INFINITY", - "double.NAN", - "float.INFINITY", - "float.NEGATIVE_INFINITY", - "float.NAN"]) - -def IsBuiltinValue(value): - return value in builtin_values - -def LookupKind(kinds, spec, scope): - """Tries to find which Kind a spec refers to, given the scope in which its - referenced. Starts checking from the narrowest scope to most general. For - example, given a struct field like - Foo.Bar x; - Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner - type 'Bar' in the struct 'Foo' in the current namespace. - - |scope| is a tuple that looks like (namespace, struct/interface), referring - to the location where the type is referenced.""" - if spec.startswith('x:'): - name = spec[2:] - for i in xrange(len(scope), -1, -1): - test_spec = 'x:' - if i > 0: - test_spec += '.'.join(scope[:i]) + '.' - test_spec += name - kind = kinds.get(test_spec) - if kind: - return kind - - return kinds.get(spec) - -def LookupValue(values, name, scope, kind): - """Like LookupKind, but for constant values.""" - # If the type is an enum, the value can be specified as a qualified name, in - # which case the form EnumName.ENUM_VALUE must be used. We use the presence - # of a '.' in the requested name to identify this. Otherwise, we prepend the - # enum name. - if isinstance(kind, mojom.Enum) and '.' not in name: - name = '%s.%s' % (kind.spec.split(':', 1)[1], name) - for i in reversed(xrange(len(scope) + 1)): - test_spec = '.'.join(scope[:i]) - if test_spec: - test_spec += '.' - test_spec += name - value = values.get(test_spec) - if value: - return value - - return values.get(name) - -def FixupExpression(module, value, scope, kind): - """Translates an IDENTIFIER into a built-in value or structured NamedValue - object.""" - if isinstance(value, tuple) and value[0] == 'IDENTIFIER': - # Allow user defined values to shadow builtins. - result = LookupValue(module.values, value[1], scope, kind) - if result: - if isinstance(result, tuple): - raise Exception('Unable to resolve expression: %r' % value[1]) - return result - if IsBuiltinValue(value[1]): - return mojom.BuiltinValue(value[1]) - return value - -def KindToData(kind): - return kind.spec - -def KindFromData(kinds, data, scope): - kind = LookupKind(kinds, data, scope) - if kind: - return kind - - if data.startswith('?'): - kind = KindFromData(kinds, data[1:], scope).MakeNullableKind() - elif data.startswith('a:'): - kind = mojom.Array(KindFromData(kinds, data[2:], scope)) - elif data.startswith('asso:'): - inner_kind = KindFromData(kinds, data[5:], scope) - if isinstance(inner_kind, mojom.InterfaceRequest): - kind = mojom.AssociatedInterfaceRequest(inner_kind) - else: - kind = mojom.AssociatedInterface(inner_kind) - elif data.startswith('a'): - colon = data.find(':') - length = int(data[1:colon]) - kind = mojom.Array(KindFromData(kinds, data[colon+1:], scope), length) - elif data.startswith('r:'): - kind = mojom.InterfaceRequest(KindFromData(kinds, data[2:], scope)) - elif data.startswith('m['): - # Isolate the two types from their brackets. - - # It is not allowed to use map as key, so there shouldn't be nested ']'s - # inside the key type spec. - key_end = data.find(']') - assert key_end != -1 and key_end < len(data) - 1 - assert data[key_end+1] == '[' and data[-1] == ']' - - first_kind = data[2:key_end] - second_kind = data[key_end+2:-1] - - kind = mojom.Map(KindFromData(kinds, first_kind, scope), - KindFromData(kinds, second_kind, scope)) - else: - kind = mojom.Kind(data) - - kinds[data] = kind - return kind - -def KindFromImport(original_kind, imported_from): - """Used with 'import module' - clones the kind imported from the given - module's namespace. Only used with Structs, Unions, Interfaces and Enums.""" - kind = copy.copy(original_kind) - # |shared_definition| is used to store various properties (see - # |AddSharedProperty()| in module.py), including |imported_from|. We don't - # want the copy to share these with the original, so copy it if necessary. - if hasattr(original_kind, 'shared_definition'): - kind.shared_definition = copy.copy(original_kind.shared_definition) - kind.imported_from = imported_from - return kind - -def ImportFromData(module, data): - import_module = data['module'] - - import_item = {} - import_item['module_name'] = import_module.name - import_item['namespace'] = import_module.namespace - import_item['module'] = import_module - - # Copy the struct kinds from our imports into the current module. - importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface) - for kind in import_module.kinds.itervalues(): - if (isinstance(kind, importable_kinds) and - kind.imported_from is None): - kind = KindFromImport(kind, import_item) - module.kinds[kind.spec] = kind - # Ditto for values. - for value in import_module.values.itervalues(): - if value.imported_from is None: - # Values don't have shared definitions (since they're not nullable), so no - # need to do anything special. - value = copy.copy(value) - value.imported_from = import_item - module.values[value.GetSpec()] = value - - return import_item - -def StructToData(struct): - data = { - istr(0, 'name'): struct.name, - istr(1, 'fields'): map(FieldToData, struct.fields), - # TODO(yzshen): EnumToData() and ConstantToData() are missing. - istr(2, 'enums'): [], - istr(3, 'constants'): [] - } - AddOptional(data, istr(4, 'attributes'), struct.attributes) - return data - -def StructFromData(module, data): - struct = mojom.Struct(module=module) - struct.name = data['name'] - struct.native_only = data['native_only'] - struct.spec = 'x:' + module.namespace + '.' + struct.name - module.kinds[struct.spec] = struct - if struct.native_only: - struct.enums = [] - struct.constants = [] - struct.fields_data = [] - else: - struct.enums = map(lambda enum: - EnumFromData(module, enum, struct), data['enums']) - struct.constants = map(lambda constant: - ConstantFromData(module, constant, struct), data['constants']) - # Stash fields data here temporarily. - struct.fields_data = data['fields'] - struct.attributes = data.get('attributes') - - # Enforce that a [Native] attribute is set to make native-only struct - # declarations more explicit. - if struct.native_only: - if not struct.attributes or not struct.attributes.get('Native', False): - raise Exception("Native-only struct declarations must include a " + - "Native attribute.") - - return struct - -def UnionToData(union): - data = { - istr(0, 'name'): union.name, - istr(1, 'fields'): map(FieldToData, union.fields) - } - AddOptional(data, istr(2, 'attributes'), union.attributes) - return data - -def UnionFromData(module, data): - union = mojom.Union(module=module) - union.name = data['name'] - union.spec = 'x:' + module.namespace + '.' + union.name - module.kinds[union.spec] = union - # Stash fields data here temporarily. - union.fields_data = data['fields'] - union.attributes = data.get('attributes') - return union - -def FieldToData(field): - data = { - istr(0, 'name'): field.name, - istr(1, 'kind'): KindToData(field.kind) - } - AddOptional(data, istr(2, 'ordinal'), field.ordinal) - AddOptional(data, istr(3, 'default'), field.default) - AddOptional(data, istr(4, 'attributes'), field.attributes) - return data - -def StructFieldFromData(module, data, struct): - field = mojom.StructField() - PopulateField(field, module, data, struct) - return field - -def UnionFieldFromData(module, data, union): - field = mojom.UnionField() - PopulateField(field, module, data, union) - return field - -def PopulateField(field, module, data, parent): - field.name = data['name'] - field.kind = KindFromData( - module.kinds, data['kind'], (module.namespace, parent.name)) - field.ordinal = data.get('ordinal') - field.default = FixupExpression( - module, data.get('default'), (module.namespace, parent.name), field.kind) - field.attributes = data.get('attributes') - -def ParameterToData(parameter): - data = { - istr(0, 'name'): parameter.name, - istr(1, 'kind'): parameter.kind.spec - } - AddOptional(data, istr(2, 'ordinal'), parameter.ordinal) - AddOptional(data, istr(3, 'default'), parameter.default) - AddOptional(data, istr(4, 'attributes'), parameter.attributes) - return data - -def ParameterFromData(module, data, interface): - parameter = mojom.Parameter() - parameter.name = data['name'] - parameter.kind = KindFromData( - module.kinds, data['kind'], (module.namespace, interface.name)) - parameter.ordinal = data.get('ordinal') - parameter.default = data.get('default') - parameter.attributes = data.get('attributes') - return parameter - -def MethodToData(method): - data = { - istr(0, 'name'): method.name, - istr(1, 'parameters'): map(ParameterToData, method.parameters) - } - if method.response_parameters is not None: - data[istr(2, 'response_parameters')] = map( - ParameterToData, method.response_parameters) - AddOptional(data, istr(3, 'ordinal'), method.ordinal) - AddOptional(data, istr(4, 'attributes'), method.attributes) - return data - -def MethodFromData(module, data, interface): - method = mojom.Method(interface, data['name'], ordinal=data.get('ordinal')) - method.parameters = map(lambda parameter: - ParameterFromData(module, parameter, interface), data['parameters']) - if data.has_key('response_parameters'): - method.response_parameters = map( - lambda parameter: ParameterFromData(module, parameter, interface), - data['response_parameters']) - method.attributes = data.get('attributes') - - # Enforce that only methods with response can have a [Sync] attribute. - if method.sync and method.response_parameters is None: - raise Exception("Only methods with response can include a [Sync] " - "attribute. If no response parameters are needed, you " - "could use an empty response parameter list, i.e., " - "\"=> ()\".") - - return method - -def InterfaceToData(interface): - data = { - istr(0, 'name'): interface.name, - istr(1, 'methods'): map(MethodToData, interface.methods), - # TODO(yzshen): EnumToData() and ConstantToData() are missing. - istr(2, 'enums'): [], - istr(3, 'constants'): [] - } - AddOptional(data, istr(4, 'attributes'), interface.attributes) - return data - -def InterfaceFromData(module, data): - interface = mojom.Interface(module=module) - interface.name = data['name'] - interface.spec = 'x:' + module.namespace + '.' + interface.name - module.kinds[interface.spec] = interface - interface.enums = map(lambda enum: - EnumFromData(module, enum, interface), data['enums']) - interface.constants = map(lambda constant: - ConstantFromData(module, constant, interface), data['constants']) - # Stash methods data here temporarily. - interface.methods_data = data['methods'] - interface.attributes = data.get('attributes') - return interface - -def EnumFieldFromData(module, enum, data, parent_kind): - field = mojom.EnumField() - field.name = data['name'] - # TODO(mpcomplete): FixupExpression should be done in the second pass, - # so constants and enums can refer to each other. - # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or - # vice versa? - if parent_kind: - field.value = FixupExpression( - module, data.get('value'), (module.namespace, parent_kind.name), enum) - else: - field.value = FixupExpression( - module, data.get('value'), (module.namespace, ), enum) - field.attributes = data.get('attributes') - value = mojom.EnumValue(module, enum, field) - module.values[value.GetSpec()] = value - return field - -def ResolveNumericEnumValues(enum_fields): - """ - Given a reference to a list of mojom.EnumField, resolves and assigns their - values to EnumField.numeric_value. - """ - - # map of <name> -> integral value - resolved_enum_values = {} - prev_value = -1 - for field in enum_fields: - # This enum value is +1 the previous enum value (e.g: BEGIN). - if field.value is None: - prev_value += 1 - - # Integral value (e.g: BEGIN = -0x1). - elif type(field.value) is str: - prev_value = int(field.value, 0) - - # Reference to a previous enum value (e.g: INIT = BEGIN). - elif type(field.value) is mojom.EnumValue: - prev_value = resolved_enum_values[field.value.name] - else: - raise Exception("Unresolved enum value.") - - resolved_enum_values[field.name] = prev_value - field.numeric_value = prev_value - -def EnumFromData(module, data, parent_kind): - enum = mojom.Enum(module=module) - enum.name = data['name'] - enum.native_only = data['native_only'] - name = enum.name - if parent_kind: - name = parent_kind.name + '.' + name - enum.spec = 'x:%s.%s' % (module.namespace, name) - enum.parent_kind = parent_kind - enum.attributes = data.get('attributes') - if enum.native_only: - enum.fields = [] - else: - enum.fields = map( - lambda field: EnumFieldFromData(module, enum, field, parent_kind), - data['fields']) - ResolveNumericEnumValues(enum.fields) - - module.kinds[enum.spec] = enum - - # Enforce that a [Native] attribute is set to make native-only enum - # declarations more explicit. - if enum.native_only: - if not enum.attributes or not enum.attributes.get('Native', False): - raise Exception("Native-only enum declarations must include a " + - "Native attribute.") - - return enum - -def ConstantFromData(module, data, parent_kind): - constant = mojom.Constant() - constant.name = data['name'] - if parent_kind: - scope = (module.namespace, parent_kind.name) - else: - scope = (module.namespace, ) - # TODO(mpcomplete): maybe we should only support POD kinds. - constant.kind = KindFromData(module.kinds, data['kind'], scope) - constant.parent_kind = parent_kind - constant.value = FixupExpression(module, data.get('value'), scope, None) - - value = mojom.ConstantValue(module, parent_kind, constant) - module.values[value.GetSpec()] = value - return constant - -def ModuleToData(module): - data = { - istr(0, 'name'): module.name, - istr(1, 'namespace'): module.namespace, - # TODO(yzshen): Imports information is missing. - istr(2, 'imports'): [], - istr(3, 'structs'): map(StructToData, module.structs), - istr(4, 'unions'): map(UnionToData, module.unions), - istr(5, 'interfaces'): map(InterfaceToData, module.interfaces), - # TODO(yzshen): EnumToData() and ConstantToData() are missing. - istr(6, 'enums'): [], - istr(7, 'constants'): [] - } - AddOptional(data, istr(8, 'attributes'), module.attributes) - return data - -def ModuleFromData(data): - module = mojom.Module() - module.kinds = {} - for kind in mojom.PRIMITIVES: - module.kinds[kind.spec] = kind - - module.values = {} - - module.name = data['name'] - module.namespace = data['namespace'] - # Imports must come first, because they add to module.kinds which is used - # by by the others. - module.imports = map( - lambda import_data: ImportFromData(module, import_data), - data['imports']) - module.attributes = data.get('attributes') - - # First pass collects kinds. - module.enums = map( - lambda enum: EnumFromData(module, enum, None), data['enums']) - module.structs = map( - lambda struct: StructFromData(module, struct), data['structs']) - module.unions = map( - lambda union: UnionFromData(module, union), data.get('unions', [])) - module.interfaces = map( - lambda interface: InterfaceFromData(module, interface), - data['interfaces']) - module.constants = map( - lambda constant: ConstantFromData(module, constant, None), - data['constants']) - - # Second pass expands fields and methods. This allows fields and parameters - # to refer to kinds defined anywhere in the mojom. - for struct in module.structs: - struct.fields = map(lambda field: - StructFieldFromData(module, field, struct), struct.fields_data) - del struct.fields_data - for union in module.unions: - union.fields = map(lambda field: - UnionFieldFromData(module, field, union), union.fields_data) - del union.fields_data - for interface in module.interfaces: - interface.methods = map(lambda method: - MethodFromData(module, method, interface), interface.methods_data) - del interface.methods_data - - return module - -def OrderedModuleFromData(data): - """Convert Mojom IR to a module. - - Args: - data: The Mojom IR as a dict. - - Returns: - A mojom.generate.module.Module object. - """ - module = ModuleFromData(data) - for interface in module.interfaces: - next_ordinal = 0 - for method in interface.methods: - if method.ordinal is None: - method.ordinal = next_ordinal - next_ordinal = method.ordinal + 1 - return module diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py deleted file mode 100644 index 096554c61a9..00000000000 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/data_tests.py +++ /dev/null @@ -1,86 +0,0 @@ -# Copyright 2013 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -import sys - -import data -import test_support - -EXPECT_EQ = test_support.EXPECT_EQ -EXPECT_TRUE = test_support.EXPECT_TRUE -RunTest = test_support.RunTest - - -def DeepEquals(d1, d2): - if d1 == d2: - return True - if d2.__class__ != d2.__class__: - return False - if isinstance(d1, dict): - if set(d1.keys()) != set(d2.keys()): - return False - for key in d1.keys(): - if not DeepEquals(d1[key], d2[key]): - return False - return True - if isinstance(d1, (list, tuple)): - if len(d1) != len(d2): - return False - for i in range(len(d1)): - if not DeepEquals(d1[i], d2[i]): - return False - return True - return False - - -test_dict = { - 'name': 'test', - 'namespace': 'testspace', - 'structs': [{ - 'name': 'teststruct', - 'fields': [ - {'name': 'testfield1', 'kind': 'i32'}, - {'name': 'testfield2', 'kind': 'a:i32', 'ordinal': 42}]}], - 'interfaces': [{ - 'name': 'Server', - 'client': None, - 'methods': [{ - 'name': 'Foo', - 'parameters': [ - {'name': 'foo', 'kind': 'i32'}, - {'name': 'bar', 'kind': 'a:x:teststruct'}], - 'ordinal': 42}]}] -} - - -def TestRead(): - module = data.ModuleFromData(test_dict) - return test_support.TestTestModule(module) - - -def TestWrite(): - module = test_support.BuildTestModule() - d = data.ModuleToData(module) - return EXPECT_TRUE(DeepEquals(test_dict, d)) - - -def TestWriteRead(): - module1 = test_support.BuildTestModule() - - dict1 = data.ModuleToData(module1) - module2 = data.ModuleFromData(dict1) - return EXPECT_TRUE(test_support.ModulesAreEqual(module1, module2)) - - -def Main(args): - errors = 0 - errors += RunTest(TestWriteRead) - errors += RunTest(TestRead) - errors += RunTest(TestWrite) - - return errors - - -if __name__ == '__main__': - sys.exit(Main(sys.argv[1:])) diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py index 9ea6cf8e1fe..66f89540126 100644 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py +++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/template_expander.py @@ -2,8 +2,7 @@ # Use of this source code is governed by a BSD-style license that can be # found in the LICENSE file. -# Based on: -# http://src.chromium.org/viewvc/blink/trunk/Source/build/scripts/template_expander.py +# Based on third_party/WebKit/Source/build/scripts/template_expander.py. import imp import os.path diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/generate/translate.py b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/translate.py new file mode 100644 index 00000000000..ffad7447a93 --- /dev/null +++ b/chromium/mojo/public/tools/bindings/pylib/mojom/generate/translate.py @@ -0,0 +1,639 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +"""Convert parse tree to AST. + +This module converts the parse tree to the AST we use for code generation. The +main entry point is OrderedModule, which gets passed the parser +representation of a mojom file. When called it's assumed that all imports have +already been parsed and converted to ASTs before. +""" + +import copy +import re + +import module as mojom +from mojom.parse import ast + +def _DuplicateName(values): + """Returns the 'name' of the first entry in |values| whose 'name' has already + been encountered. If there are no duplicates, returns None.""" + names = set() + for value in values: + if value.name in names: + return value.name + names.add(value.name) + return None + +def _ElemsOfType(elems, elem_type, scope): + """Find all elements of the given type. + + Args: + elems: {Sequence[Any]} Sequence of elems. + elem_type: {Type[C]} Extract all elems of this type. + scope: {str} The name of the surrounding scope (e.g. struct + definition). Used in error messages. + + Returns: + {List[C]} All elems of matching type. + """ + assert isinstance(elem_type, type) + result = [elem for elem in elems if isinstance(elem, elem_type)] + duplicate_name = _DuplicateName(result) + if duplicate_name: + raise Exception('Names in mojom must be unique within a scope. The name ' + '"%s" is used more than once within the scope "%s".' % + (duplicate_name, scope)) + return result + +def _MapKind(kind): + map_to_kind = {'bool': 'b', + 'int8': 'i8', + 'int16': 'i16', + 'int32': 'i32', + 'int64': 'i64', + 'uint8': 'u8', + 'uint16': 'u16', + 'uint32': 'u32', + 'uint64': 'u64', + 'float': 'f', + 'double': 'd', + 'string': 's', + 'handle': 'h', + 'handle<data_pipe_consumer>': 'h:d:c', + 'handle<data_pipe_producer>': 'h:d:p', + 'handle<message_pipe>': 'h:m', + 'handle<shared_buffer>': 'h:s'} + if kind.endswith('?'): + base_kind = _MapKind(kind[0:-1]) + # NOTE: This doesn't rule out enum types. Those will be detected later, when + # cross-reference is established. + reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso') + if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds: + raise Exception( + 'A type (spec "%s") cannot be made nullable' % base_kind) + return '?' + base_kind + if kind.endswith('}'): + lbracket = kind.rfind('{') + value = kind[0:lbracket] + return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']' + if kind.endswith(']'): + lbracket = kind.rfind('[') + typename = kind[0:lbracket] + return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename) + if kind.endswith('&'): + return 'r:' + _MapKind(kind[0:-1]) + if kind.startswith('asso<'): + assert kind.endswith('>') + return 'asso:' + _MapKind(kind[5:-1]) + if kind in map_to_kind: + return map_to_kind[kind] + return 'x:' + kind + +def _AttributeListToDict(attribute_list): + if attribute_list is None: + return None + assert isinstance(attribute_list, ast.AttributeList) + # TODO(vtl): Check for duplicate keys here. + return dict([(attribute.key, attribute.value) + for attribute in attribute_list]) + +builtin_values = frozenset([ + "double.INFINITY", + "double.NEGATIVE_INFINITY", + "double.NAN", + "float.INFINITY", + "float.NEGATIVE_INFINITY", + "float.NAN"]) + +def _IsBuiltinValue(value): + return value in builtin_values + +def _LookupKind(kinds, spec, scope): + """Tries to find which Kind a spec refers to, given the scope in which its + referenced. Starts checking from the narrowest scope to most general. For + example, given a struct field like + Foo.Bar x; + Foo.Bar could refer to the type 'Bar' in the 'Foo' namespace, or an inner + type 'Bar' in the struct 'Foo' in the current namespace. + + |scope| is a tuple that looks like (namespace, struct/interface), referring + to the location where the type is referenced.""" + if spec.startswith('x:'): + name = spec[2:] + for i in xrange(len(scope), -1, -1): + test_spec = 'x:' + if i > 0: + test_spec += '.'.join(scope[:i]) + '.' + test_spec += name + kind = kinds.get(test_spec) + if kind: + return kind + + return kinds.get(spec) + +def _LookupValue(values, name, scope, kind): + """Like LookupKind, but for constant values.""" + # If the type is an enum, the value can be specified as a qualified name, in + # which case the form EnumName.ENUM_VALUE must be used. We use the presence + # of a '.' in the requested name to identify this. Otherwise, we prepend the + # enum name. + if isinstance(kind, mojom.Enum) and '.' not in name: + name = '%s.%s' % (kind.spec.split(':', 1)[1], name) + for i in reversed(xrange(len(scope) + 1)): + test_spec = '.'.join(scope[:i]) + if test_spec: + test_spec += '.' + test_spec += name + value = values.get(test_spec) + if value: + return value + + return values.get(name) + +def _FixupExpression(module, value, scope, kind): + """Translates an IDENTIFIER into a built-in value or structured NamedValue + object.""" + if isinstance(value, tuple) and value[0] == 'IDENTIFIER': + # Allow user defined values to shadow builtins. + result = _LookupValue(module.values, value[1], scope, kind) + if result: + if isinstance(result, tuple): + raise Exception('Unable to resolve expression: %r' % value[1]) + return result + if _IsBuiltinValue(value[1]): + return mojom.BuiltinValue(value[1]) + return value + +def _Kind(kinds, spec, scope): + """Convert a type name into a mojom.Kind object. + + As a side-effect this function adds the result to 'kinds'. + + Args: + kinds: {Dict[str, mojom.Kind]} All known kinds up to this point, indexed by + their names. + spec: {str} A name uniquely identifying a type. + scope: {Tuple[str, str]} A tuple that looks like (namespace, + struct/interface), referring to the location where the type is + referenced. + + Returns: + {mojom.Kind} The type corresponding to 'spec'. + """ + kind = _LookupKind(kinds, spec, scope) + if kind: + return kind + + if spec.startswith('?'): + kind = _Kind(kinds, spec[1:], scope).MakeNullableKind() + elif spec.startswith('a:'): + kind = mojom.Array(_Kind(kinds, spec[2:], scope)) + elif spec.startswith('asso:'): + inner_kind = _Kind(kinds, spec[5:], scope) + if isinstance(inner_kind, mojom.InterfaceRequest): + kind = mojom.AssociatedInterfaceRequest(inner_kind) + else: + kind = mojom.AssociatedInterface(inner_kind) + elif spec.startswith('a'): + colon = spec.find(':') + length = int(spec[1:colon]) + kind = mojom.Array(_Kind(kinds, spec[colon+1:], scope), length) + elif spec.startswith('r:'): + kind = mojom.InterfaceRequest(_Kind(kinds, spec[2:], scope)) + elif spec.startswith('m['): + # Isolate the two types from their brackets. + + # It is not allowed to use map as key, so there shouldn't be nested ']'s + # inside the key type spec. + key_end = spec.find(']') + assert key_end != -1 and key_end < len(spec) - 1 + assert spec[key_end+1] == '[' and spec[-1] == ']' + + first_kind = spec[2:key_end] + second_kind = spec[key_end+2:-1] + + kind = mojom.Map(_Kind(kinds, first_kind, scope), + _Kind(kinds, second_kind, scope)) + else: + kind = mojom.Kind(spec) + + kinds[spec] = kind + return kind + +def _KindFromImport(original_kind, imported_from): + """Used with 'import module' - clones the kind imported from the given + module's namespace. Only used with Structs, Unions, Interfaces and Enums.""" + kind = copy.copy(original_kind) + # |shared_definition| is used to store various properties (see + # |AddSharedProperty()| in module.py), including |imported_from|. We don't + # want the copy to share these with the original, so copy it if necessary. + if hasattr(original_kind, 'shared_definition'): + kind.shared_definition = copy.copy(original_kind.shared_definition) + kind.imported_from = imported_from + return kind + +def _Import(module, import_module): + import_item = {} + import_item['module_name'] = import_module.name + import_item['namespace'] = import_module.namespace + import_item['module'] = import_module + + # Copy the struct kinds from our imports into the current module. + importable_kinds = (mojom.Struct, mojom.Union, mojom.Enum, mojom.Interface) + for kind in import_module.kinds.itervalues(): + if (isinstance(kind, importable_kinds) and + kind.imported_from is None): + kind = _KindFromImport(kind, import_item) + module.kinds[kind.spec] = kind + # Ditto for values. + for value in import_module.values.itervalues(): + if value.imported_from is None: + # Values don't have shared definitions (since they're not nullable), so no + # need to do anything special. + value = copy.copy(value) + value.imported_from = import_item + module.values[value.GetSpec()] = value + + return import_item + +def _Struct(module, parsed_struct): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_struct: {ast.Struct} Parsed struct. + + Returns: + {mojom.Struct} AST struct. + """ + struct = mojom.Struct(module=module) + struct.name = parsed_struct.name + struct.native_only = parsed_struct.body is None + struct.spec = 'x:' + module.namespace + '.' + struct.name + module.kinds[struct.spec] = struct + if struct.native_only: + struct.enums = [] + struct.constants = [] + struct.fields_data = [] + else: + struct.enums = map( + lambda enum: _Enum(module, enum, struct), + _ElemsOfType(parsed_struct.body, ast.Enum, parsed_struct.name)) + struct.constants = map( + lambda constant: _Constant(module, constant, struct), + _ElemsOfType(parsed_struct.body, ast.Const, parsed_struct.name)) + # Stash fields parsed_struct here temporarily. + struct.fields_data = _ElemsOfType( + parsed_struct.body, ast.StructField, parsed_struct.name) + struct.attributes = _AttributeListToDict(parsed_struct.attribute_list) + + # Enforce that a [Native] attribute is set to make native-only struct + # declarations more explicit. + if struct.native_only: + if not struct.attributes or not struct.attributes.get('Native', False): + raise Exception("Native-only struct declarations must include a " + + "Native attribute.") + + return struct + +def _Union(module, parsed_union): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_union: {ast.Union} Parsed union. + + Returns: + {mojom.Union} AST union. + """ + union = mojom.Union(module=module) + union.name = parsed_union.name + union.spec = 'x:' + module.namespace + '.' + union.name + module.kinds[union.spec] = union + # Stash fields parsed_union here temporarily. + union.fields_data = _ElemsOfType( + parsed_union.body, ast.UnionField, parsed_union.name) + union.attributes = _AttributeListToDict(parsed_union.attribute_list) + return union + +def _StructField(module, parsed_field, struct): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_field: {ast.StructField} Parsed struct field. + struct: {mojom.Struct} Struct this field belongs to. + + Returns: + {mojom.StructField} AST struct field. + """ + field = mojom.StructField() + field.name = parsed_field.name + field.kind = _Kind( + module.kinds, _MapKind(parsed_field.typename), + (module.namespace, struct.name)) + field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None + field.default = _FixupExpression( + module, parsed_field.default_value, (module.namespace, struct.name), + field.kind) + field.attributes = _AttributeListToDict(parsed_field.attribute_list) + return field + +def _UnionField(module, parsed_field, union): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_field: {ast.UnionField} Parsed union field. + union: {mojom.Union} Union this fields belong to. + + Returns: + {mojom.UnionField} AST union. + """ + field = mojom.UnionField() + field.name = parsed_field.name + field.kind = _Kind( + module.kinds, _MapKind(parsed_field.typename), + (module.namespace, union.name)) + field.ordinal = parsed_field.ordinal.value if parsed_field.ordinal else None + field.default = _FixupExpression( + module, None, (module.namespace, union.name), field.kind) + field.attributes = _AttributeListToDict(parsed_field.attribute_list) + return field + +def _Parameter(module, parsed_param, interface): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_param: {ast.Parameter} Parsed parameter. + union: {mojom.Interface} Interface this parameter belongs to. + + Returns: + {mojom.Parameter} AST parameter. + """ + parameter = mojom.Parameter() + parameter.name = parsed_param.name + parameter.kind = _Kind( + module.kinds, _MapKind(parsed_param.typename), + (module.namespace, interface.name)) + parameter.ordinal = ( + parsed_param.ordinal.value if parsed_param.ordinal else None) + parameter.default = None # TODO(tibell): We never have these. Remove field? + parameter.attributes = _AttributeListToDict(parsed_param.attribute_list) + return parameter + +def _Method(module, parsed_method, interface): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_method: {ast.Method} Parsed method. + interface: {mojom.Interface} Interface this method belongs to. + + Returns: + {mojom.Method} AST method. + """ + method = mojom.Method( + interface, parsed_method.name, + ordinal=parsed_method.ordinal.value if parsed_method.ordinal else None) + method.parameters = map( + lambda parameter: _Parameter(module, parameter, interface), + parsed_method.parameter_list) + if parsed_method.response_parameter_list is not None: + method.response_parameters = map( + lambda parameter: _Parameter(module, parameter, interface), + parsed_method.response_parameter_list) + method.attributes = _AttributeListToDict(parsed_method.attribute_list) + + # Enforce that only methods with response can have a [Sync] attribute. + if method.sync and method.response_parameters is None: + raise Exception("Only methods with response can include a [Sync] " + "attribute. If no response parameters are needed, you " + "could use an empty response parameter list, i.e., " + "\"=> ()\".") + + return method + +def _Interface(module, parsed_iface): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_iface: {ast.Interface} Parsed interface. + + Returns: + {mojom.Interface} AST interface. + """ + interface = mojom.Interface(module=module) + interface.name = parsed_iface.name + interface.spec = 'x:' + module.namespace + '.' + interface.name + module.kinds[interface.spec] = interface + interface.enums = map( + lambda enum: _Enum(module, enum, interface), + _ElemsOfType(parsed_iface.body, ast.Enum, parsed_iface.name)) + interface.constants = map( + lambda constant: _Constant(module, constant, interface), + _ElemsOfType(parsed_iface.body, ast.Const, parsed_iface.name)) + # Stash methods parsed_iface here temporarily. + interface.methods_data = _ElemsOfType( + parsed_iface.body, ast.Method, parsed_iface.name) + interface.attributes = _AttributeListToDict(parsed_iface.attribute_list) + return interface + +def _EnumField(module, enum, parsed_field, parent_kind): + """ + Args: + module: {mojom.Module} Module currently being constructed. + enum: {mojom.Enum} Enum this field belongs to. + parsed_field: {ast.EnumValue} Parsed enum value. + parent_kind: {mojom.Kind} The enclosing type. + + Returns: + {mojom.EnumField} AST enum field. + """ + field = mojom.EnumField() + field.name = parsed_field.name + # TODO(mpcomplete): FixupExpression should be done in the second pass, + # so constants and enums can refer to each other. + # TODO(mpcomplete): But then, what if constants are initialized to an enum? Or + # vice versa? + if parent_kind: + field.value = _FixupExpression( + module, parsed_field.value, (module.namespace, parent_kind.name), enum) + else: + field.value = _FixupExpression( + module, parsed_field.value, (module.namespace, ), enum) + field.attributes = _AttributeListToDict(parsed_field.attribute_list) + value = mojom.EnumValue(module, enum, field) + module.values[value.GetSpec()] = value + return field + +def _ResolveNumericEnumValues(enum_fields): + """ + Given a reference to a list of mojom.EnumField, resolves and assigns their + values to EnumField.numeric_value. + """ + + # map of <name> -> integral value + resolved_enum_values = {} + prev_value = -1 + for field in enum_fields: + # This enum value is +1 the previous enum value (e.g: BEGIN). + if field.value is None: + prev_value += 1 + + # Integral value (e.g: BEGIN = -0x1). + elif type(field.value) is str: + prev_value = int(field.value, 0) + + # Reference to a previous enum value (e.g: INIT = BEGIN). + elif type(field.value) is mojom.EnumValue: + prev_value = resolved_enum_values[field.value.name] + else: + raise Exception("Unresolved enum value.") + + resolved_enum_values[field.name] = prev_value + field.numeric_value = prev_value + +def _Enum(module, parsed_enum, parent_kind): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_enum: {ast.Enum} Parsed enum. + + Returns: + {mojom.Enum} AST enum. + """ + enum = mojom.Enum(module=module) + enum.name = parsed_enum.name + enum.native_only = parsed_enum.enum_value_list is None + name = enum.name + if parent_kind: + name = parent_kind.name + '.' + name + enum.spec = 'x:%s.%s' % (module.namespace, name) + enum.parent_kind = parent_kind + enum.attributes = _AttributeListToDict(parsed_enum.attribute_list) + if enum.native_only: + enum.fields = [] + else: + enum.fields = map( + lambda field: _EnumField(module, enum, field, parent_kind), + parsed_enum.enum_value_list) + _ResolveNumericEnumValues(enum.fields) + + module.kinds[enum.spec] = enum + + # Enforce that a [Native] attribute is set to make native-only enum + # declarations more explicit. + if enum.native_only: + if not enum.attributes or not enum.attributes.get('Native', False): + raise Exception("Native-only enum declarations must include a " + + "Native attribute.") + + return enum + +def _Constant(module, parsed_const, parent_kind): + """ + Args: + module: {mojom.Module} Module currently being constructed. + parsed_const: {ast.Const} Parsed constant. + + Returns: + {mojom.Constant} AST constant. + """ + constant = mojom.Constant() + constant.name = parsed_const.name + if parent_kind: + scope = (module.namespace, parent_kind.name) + else: + scope = (module.namespace, ) + # TODO(mpcomplete): maybe we should only support POD kinds. + constant.kind = _Kind(module.kinds, _MapKind(parsed_const.typename), scope) + constant.parent_kind = parent_kind + constant.value = _FixupExpression(module, parsed_const.value, scope, None) + + value = mojom.ConstantValue(module, parent_kind, constant) + module.values[value.GetSpec()] = value + return constant + +def _Module(tree, name, imports): + """ + Args: + tree: {ast.Mojom} The parse tree. + name: {str} The mojom filename, excluding the path. + imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in + the import list, to already processed modules. Used to process imports. + + Returns: + {mojom.Module} An AST for the mojom. + """ + module = mojom.Module() + module.kinds = {} + for kind in mojom.PRIMITIVES: + module.kinds[kind.spec] = kind + + module.values = {} + + module.name = name + module.namespace = tree.module.name[1] if tree.module else '' + # Imports must come first, because they add to module.kinds which is used + # by by the others. + module.imports = [ + _Import(module, imports[imp.import_filename]) + for imp in tree.import_list] + if tree.module and tree.module.attribute_list: + assert isinstance(tree.module.attribute_list, ast.AttributeList) + # TODO(vtl): Check for duplicate keys here. + module.attributes = dict((attribute.key, attribute.value) + for attribute in tree.module.attribute_list) + + # First pass collects kinds. + module.enums = map( + lambda enum: _Enum(module, enum, None), + _ElemsOfType(tree.definition_list, ast.Enum, name)) + module.structs = map( + lambda struct: _Struct(module, struct), + _ElemsOfType(tree.definition_list, ast.Struct, name)) + module.unions = map( + lambda union: _Union(module, union), + _ElemsOfType(tree.definition_list, ast.Union, name)) + module.interfaces = map( + lambda interface: _Interface(module, interface), + _ElemsOfType(tree.definition_list, ast.Interface, name)) + module.constants = map( + lambda constant: _Constant(module, constant, None), + _ElemsOfType(tree.definition_list, ast.Const, name)) + + # Second pass expands fields and methods. This allows fields and parameters + # to refer to kinds defined anywhere in the mojom. + for struct in module.structs: + struct.fields = map(lambda field: + _StructField(module, field, struct), struct.fields_data) + del struct.fields_data + for union in module.unions: + union.fields = map(lambda field: + _UnionField(module, field, union), union.fields_data) + del union.fields_data + for interface in module.interfaces: + interface.methods = map(lambda method: + _Method(module, method, interface), interface.methods_data) + del interface.methods_data + + return module + +def OrderedModule(tree, name, imports): + """Convert parse tree to AST module. + + Args: + tree: {ast.Mojom} The parse tree. + name: {str} The mojom filename, excluding the path. + imports: {Dict[str, mojom.Module]} Mapping from filenames, as they appear in + the import list, to already processed modules. Used to process imports. + + Returns: + {mojom.Module} An AST for the mojom. + """ + module = _Module(tree, name, imports) + for interface in module.interfaces: + next_ordinal = 0 + for method in interface.methods: + if method.ordinal is None: + method.ordinal = next_ordinal + next_ordinal = method.ordinal + 1 + return module diff --git a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py b/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py deleted file mode 100644 index 66e8443f033..00000000000 --- a/chromium/mojo/public/tools/bindings/pylib/mojom/parse/translate.py +++ /dev/null @@ -1,236 +0,0 @@ -# Copyright 2014 The Chromium Authors. All rights reserved. -# Use of this source code is governed by a BSD-style license that can be -# found in the LICENSE file. - -"""Translates parse tree to Mojom IR.""" - -import re - -from . import ast - - -def _DuplicateName(values): - """Returns the 'name' of the first entry in |values| whose 'name' has already - been encountered. If there are no duplicates, returns None.""" - names = set() - for value in values: - if value['name'] in names: - return value['name'] - names.add(value['name']) - return None - -def _MapTreeForType(func, tree, type_to_map, scope): - assert isinstance(type_to_map, type) - if not tree: - return [] - result = [func(subtree) - for subtree in tree if isinstance(subtree, type_to_map)] - duplicate_name = _DuplicateName(result) - if duplicate_name: - raise Exception('Names in mojom must be unique within a scope. The name ' - '"%s" is used more than once within the scope "%s".' % - (duplicate_name, scope)) - return result - -def _MapKind(kind): - map_to_kind = {'bool': 'b', - 'int8': 'i8', - 'int16': 'i16', - 'int32': 'i32', - 'int64': 'i64', - 'uint8': 'u8', - 'uint16': 'u16', - 'uint32': 'u32', - 'uint64': 'u64', - 'float': 'f', - 'double': 'd', - 'string': 's', - 'handle': 'h', - 'handle<data_pipe_consumer>': 'h:d:c', - 'handle<data_pipe_producer>': 'h:d:p', - 'handle<message_pipe>': 'h:m', - 'handle<shared_buffer>': 'h:s'} - if kind.endswith('?'): - base_kind = _MapKind(kind[0:-1]) - # NOTE: This doesn't rule out enum types. Those will be detected later, when - # cross-reference is established. - reference_kinds = ('m', 's', 'h', 'a', 'r', 'x', 'asso') - if re.split('[^a-z]', base_kind, 1)[0] not in reference_kinds: - raise Exception( - 'A type (spec "%s") cannot be made nullable' % base_kind) - return '?' + base_kind - if kind.endswith('}'): - lbracket = kind.rfind('{') - value = kind[0:lbracket] - return 'm[' + _MapKind(kind[lbracket+1:-1]) + '][' + _MapKind(value) + ']' - if kind.endswith(']'): - lbracket = kind.rfind('[') - typename = kind[0:lbracket] - return 'a' + kind[lbracket+1:-1] + ':' + _MapKind(typename) - if kind.endswith('&'): - return 'r:' + _MapKind(kind[0:-1]) - if kind.startswith('asso<'): - assert kind.endswith('>') - return 'asso:' + _MapKind(kind[5:-1]) - if kind in map_to_kind: - return map_to_kind[kind] - return 'x:' + kind - -def _AddOptional(dictionary, key, value): - if value is not None: - dictionary[key] = value; - -def _AttributeListToDict(attribute_list): - if attribute_list is None: - return None - assert isinstance(attribute_list, ast.AttributeList) - # TODO(vtl): Check for duplicate keys here. - return dict([(attribute.key, attribute.value) - for attribute in attribute_list]) - -def _EnumToDict(enum): - def EnumValueToDict(enum_value): - assert isinstance(enum_value, ast.EnumValue) - data = {'name': enum_value.name} - _AddOptional(data, 'value', enum_value.value) - _AddOptional(data, 'attributes', - _AttributeListToDict(enum_value.attribute_list)) - return data - - assert isinstance(enum, ast.Enum) - data = {'name': enum.name, - 'native_only': enum.enum_value_list is None } - if not data['native_only']: - data.update({'fields': map(EnumValueToDict, enum.enum_value_list)}) - _AddOptional(data, 'attributes', _AttributeListToDict(enum.attribute_list)) - return data - -def _ConstToDict(const): - assert isinstance(const, ast.Const) - return {'name': const.name, - 'kind': _MapKind(const.typename), - 'value': const.value} - - -class _MojomBuilder(object): - def __init__(self): - self.mojom = {} - - def Build(self, tree, name): - def StructToDict(struct): - def StructFieldToDict(struct_field): - assert isinstance(struct_field, ast.StructField) - data = {'name': struct_field.name, - 'kind': _MapKind(struct_field.typename)} - _AddOptional(data, 'ordinal', - struct_field.ordinal.value - if struct_field.ordinal else None) - _AddOptional(data, 'default', struct_field.default_value) - _AddOptional(data, 'attributes', - _AttributeListToDict(struct_field.attribute_list)) - return data - - assert isinstance(struct, ast.Struct) - data = {'name': struct.name, - 'native_only': struct.body is None} - if not data['native_only']: - data.update({ - 'fields': _MapTreeForType(StructFieldToDict, struct.body, - ast.StructField, struct.name), - 'enums': _MapTreeForType(_EnumToDict, struct.body, ast.Enum, - struct.name), - 'constants': _MapTreeForType(_ConstToDict, struct.body, - ast.Const, struct.name)}) - _AddOptional(data, 'attributes', - _AttributeListToDict(struct.attribute_list)) - return data - - def UnionToDict(union): - def UnionFieldToDict(union_field): - assert isinstance(union_field, ast.UnionField) - data = {'name': union_field.name, - 'kind': _MapKind(union_field.typename)} - _AddOptional(data, 'ordinal', - union_field.ordinal.value - if union_field.ordinal else None) - _AddOptional(data, 'attributes', - _AttributeListToDict(union_field.attribute_list)) - return data - - assert isinstance(union, ast.Union) - data = {'name': union.name, - 'fields': _MapTreeForType(UnionFieldToDict, union.body, - ast.UnionField, union.name)} - _AddOptional(data, 'attributes', - _AttributeListToDict(union.attribute_list)) - return data - - def InterfaceToDict(interface): - def MethodToDict(method): - def ParameterToDict(param): - assert isinstance(param, ast.Parameter) - data = {'name': param.name, - 'kind': _MapKind(param.typename)} - _AddOptional(data, 'ordinal', - param.ordinal.value if param.ordinal else None) - _AddOptional(data, 'attributes', - _AttributeListToDict(param.attribute_list)) - return data - - assert isinstance(method, ast.Method) - data = {'name': method.name, - 'parameters': map(ParameterToDict, method.parameter_list)} - if method.response_parameter_list is not None: - data['response_parameters'] = map(ParameterToDict, - method.response_parameter_list) - _AddOptional(data, 'ordinal', - method.ordinal.value if method.ordinal else None) - _AddOptional(data, 'attributes', - _AttributeListToDict(method.attribute_list)) - return data - - assert isinstance(interface, ast.Interface) - data = {'name': interface.name, - 'methods': _MapTreeForType(MethodToDict, interface.body, - ast.Method, interface.name), - 'enums': _MapTreeForType(_EnumToDict, interface.body, ast.Enum, - interface.name), - 'constants': _MapTreeForType(_ConstToDict, interface.body, - ast.Const, interface.name)} - _AddOptional(data, 'attributes', - _AttributeListToDict(interface.attribute_list)) - return data - - assert isinstance(tree, ast.Mojom) - self.mojom['name'] = name - self.mojom['namespace'] = tree.module.name[1] if tree.module else '' - self.mojom['imports'] = \ - [{'filename': imp.import_filename} for imp in tree.import_list] - self.mojom['structs'] = \ - _MapTreeForType(StructToDict, tree.definition_list, ast.Struct, name) - self.mojom['unions'] = \ - _MapTreeForType(UnionToDict, tree.definition_list, ast.Union, name) - self.mojom['interfaces'] = \ - _MapTreeForType(InterfaceToDict, tree.definition_list, ast.Interface, - name) - self.mojom['enums'] = \ - _MapTreeForType(_EnumToDict, tree.definition_list, ast.Enum, name) - self.mojom['constants'] = \ - _MapTreeForType(_ConstToDict, tree.definition_list, ast.Const, name) - _AddOptional(self.mojom, 'attributes', - _AttributeListToDict(tree.module.attribute_list) - if tree.module else None) - return self.mojom - - -def Translate(tree, name): - """Translate AST to Mojom IR. - - Args: - tree: The AST as a mojom.parse.ast.Mojom object. - name: The filename as a str. - - Returns: - The Mojom IR as a dict. - """ - return _MojomBuilder().Build(tree, name) |